I found what I think is a very interesting approach to your question in "Object-Oriented Modeling and Design", by Rumbaugh et al. The edition I have is a bit
old (1991) but I think the concepts are still quite applicable. Also, I cite
from this book but I guess you can find similar concepts by other authors and
in more recent pubblications.
In Chapter 6, Functional Modeling, the authors explain that building a functional model is only one aspect of the analysis and design of an application. The other two models are the object model (the structure of your system, in terms of objects, their associations, and their operations / methods) and the dynamic model (systems states, events, state transitions).
The functional model "shows how output values in a computation are derived from input values" (page 123 in my 1991-edition). In other words, even though you have a lot of state (e.g. files, database) that you modify, and a lot of user input (events) that may guide your computation, you can identify the purely functional aspects in your application, with clear inputs, outputs, and data transformations, and no side effects (the side effect may consist in storing the result of a computation, but this is a separate step).
Further, on page 137, there is a section about the relation between functional, object, and dynamic models. I cite again: "The functional model shows what has to be done by a system. [cut] The object model shows the doers - the objects. The dynamic model shows the sequence in which the operations are performed."
So, these are just some rough ideas (you can do some reading on object-oriented design to have more details), but I think you can apply the following guidelines to the programming of a web application:
- Try to identify data flows and transformations inside your application, with clear inputs (e.g. a database table) and clear output (e.g. a GUI table).
- Clearly separate in your code the parts that (1) only read / query data (2) only write (save or update) data (3) only transform data.
All procedures that are in category (3) should
- Either produce a completely new data object (the result) without changing the inputs or any other data in the environment;
- or change an input data object and return it as the result (the input value is destroyed and a new output value is created; for efficiency reasons one common data object is used for both input and output), again without changing the remaining inputs or any other data in the environment.
So, IMO even if you are developing an application that has a lot of input / output with the user, other applications, and mass storage, you can still try to isolate its functional parts and implement them more cleanly.