Fred Brooks' The Mythical Man Month is one of my favorite software engineering books. I first read it in my senior software engineering class at Rutgers. From time to time in my daily routine of software implementaion, I flash back to bits and pieces from the book and from the class.
One of the most relevant issues that Brooks' touches upon is the issue of communication and how it impacts productivity. Of this, Brooks writes:
The added burden of communication is made up of two parts, training and intercommunication. Each worker must be trained in the technology, the goals of the effort, the overall strategy, and the plan of work. This training cannot be partitioned, so this part of the added effort varies linearly with the number of workers.
Intercommunicaion is worse. If each part of the task must be separately coordinated with each other part, the effort increases as n(n-1)/2. Three workers require three times as much pairwise intercommunication as two; four require six times as much as two. If, moreoever, there need to be conferences among three, four, etc., workers to resolve things jointly, matters get worse yet. The added effort of communicating may fully counteract the division of the original task...
This issue seems particularly relevant today as teams are more geographically dispersed than ever (whether due to working with offshore teams or with developers working across the country). As you increase the number of developers working on dependent and communicating components, you dramatically increase the development time.
One of the most common approaches to working around this is to partition the work such that each developer is responsible for minimal cross component communications (each developer implements a full stack).
The approach works because there is less turnaround and less miscommunication between developers. It reduces dependencies between one component and another, allowing a develper to progress as fast as she can implement the requisite bits. Need to store an additional field? No problem. Add a column, add it to your data model, and then render it in the UI. No need to ping someone else or write a change request; just make it happen.
The downfall of this approach is that it leads to a lot of duplicate code for common logic; it tends to work well for a very small team with geographic proximity, but breaks down very quickly as the number of developers or geographic proximity increases. A developer writing data access for one component may have her own practices while another will abide by a completely different set of practices. In the long run, this leads to code that's hard to maintain as well as adding much more complexity to the product itself.
Not only that, any given developer may not be suited for developing a given part of an application stack. A UI developer will be much more proficient at and capable of designing an attractive, easy to use UI than a backend service developer. An application developer may be capable of writing a much more cohesive domain layer and business layer than a database developer.
Service contracts are an oft touted solution. The promise is that by agreeing on a service contract, each side can develop independently of a concrete implementation of the other, so long as the contract is followed to a T. However, at least in my experience, this is usually far from the truth, especially if you work in a team where the contract itself is rarely stable for more than a day or two. UI tweaks or new features can cause dramatic changes in the service (for example, requiring more data or a change to the model) which comes at a cost of time spent on change requests and communicating those changes to the other side.
For the time being, I've come to conclude that full stack development (hmm...maybe with the exception of the UI layer) is the best approach, so long as it's supported by the necessary tools, templates, and frameworks; it's all about making it easy to make the guts work (and to do so in a consistent manner) so that the implementation differences where it matters is minimized (for example, data access). Enter code generation, automation, and application frameworks to the rescue. Tools like MyGeneration, Spring, Smart Client Software Factory, and Enterprise Library are really the answer but I've found it quite difficult to get team members to buy into embracing these tools (it's one thing to use a framework; it's another, entirely, to embrace a framework or tool). When properly wielded with a cohesive software architecture, you can get the best of both worlds: a coherent codebase with common patterns across component stacks while allowing developers to work with less dependencies.
What do you think? How do your teams handle working on multiple dependent components?