There is a difference between a team that is too small and a build that has no shape. The first is answered by hiring. The second gets worse when you hire, and the reason it gets worse is that every new pair of hands has to decide, unaided and on the spot, what the software is supposed to look like.
A European banking group's IT services subsidiary commissioned an independent post-mortem review after an outsourced build of a customer-facing web application never completed acceptance testing and the contract was terminated. The review covered the contract, the project artefacts, the people through interviews, and the delivered code. Its section on team composition carries one sentence that explains more of the technical findings than the technical section manages on its own. The development team was scaled from twenty to thirty on a single occasion, and the review recorded the effect as a loss of coherence: groups that worked on their own piece in isolation, developing it downward through their own layers, without anyone holding the picture of the whole.
The challenge
Read that alongside the contract and the sequence stops looking like an accident. Before a line was written, the two parties had estimated the same scope at roughly a thousand person-days on one side and over two and a half thousand on the other. That is not a negotiating gap. It is two organisations describing two different pieces of software, and it should have been the first signal that nobody had yet agreed what was being built.
The instrument that would have settled it was in the contract. A system design document, covering coding standards, design principles and the reasoning behind the approach, was priced at 140 person-days. The review looked for it and found no evidence it had ever been produced, nor any evidence of the method behind the effort estimates. So the build began with a disputed size, no written architecture, and no traceable basis for either.
At twenty, that is survivable in the way a lot of bad practice is survivable: the shape lives in a few people's heads, and those people sit in the same conversations. At thirty it is not, because the shape now has to travel to people who were not in those conversations, and there is nothing for it to travel in. What arrives instead is ten developers doing the sensible thing: building the part in front of them in whatever style the person next to them uses.
When quality problems became visible, the response was to add more people. The review is direct about this: resources were added instead of the design of a poorly built system being examined and fixed. It also notes that the new arrivals struggled to get onboarded, which is the second half of the same problem. There was nothing to onboard them into. A senior architect was requested once the code quality problems had been escalated, and it took more than two months for one to be put on the project. By the time the architect arrived the review judged it too late, which is a polite way of saying that the shape had already set.
The approach
The code review is where the split becomes physical, and it is worth reading as evidence of team structure rather than as a list of defects.
Duplicated code was found in two distinct forms: near-identical lines that differed in small ways and should have been a private function, and blocks copied into different areas of the codebase that should have been a shared service call. Both are what parallel groups produce when there is no owned centre to put a shared thing in. The consequence recorded is the operationally expensive one: a bug fix now has to be propagated across an unknown number of files.
Caching told the same story in one snapshot. Part of the caching layer had been rewritten during a refactoring exercise, and part had been left on the old design because of time pressure. So two caching designs ran side by side, applied case by case rather than uniformly, with the local cache used less as an optimisation and more as shared memory between parts of the application that had no other way to reach each other. Data arrived from the server asynchronously and the code consuming it had no way to know when. The review records the result as erratic behaviour, with reads and writes landing in a random order so a screen sometimes shows old data and sometimes new.
The sharpest single item is the smallest. In places, the team had modified the source of the front-end framework itself rather than use the extension mechanism the framework provides for adding behaviour. Nobody with a view of the whole system agrees to that, because the cost of it lands on everyone else later. It is precisely the decision a group makes when its horizon ends at its own module and its deadline is this sprint. Alongside it sat dependencies resolved implicitly rather than declared, and synchronous and asynchronous code mixed in the same place.
None of those are exotic mistakes. They are the mistakes of people working competently without a shared model, which is a management artefact, not a talent one.
The outcome
What this engagement produced was a review: findings across delivery approach, project management and technical quality, plus recommendations. No remediation was carried out and no application was repaired. The build was terminated. The numbers above are contract terms, headcounts and code findings, not results of anything we implemented.
The part worth carrying forward is the failure mode, because it has not aged and it has changed address.
The same shape now shows up in machine learning pipelines more often than in application code. A data science group is doing well, gets funded, and grows from a handful to a few dozen across several squads. Within two quarters there are four ways to compute the same customer attribute, three feature pipelines that disagree at the third decimal place, and two deployment paths, one of which nobody will admit to owning. Feature stores exist because of exactly this, and so does most of what gets filed under MLOps: not because computing a feature is hard, but because computing it in one agreed place is the only thing that lets you add people without adding variants.
The cheap instrumentation is available now in a way it was not then. Commit and ticket events carry the divergence before any human notices it, and process mining over them shows where two groups are solving the same problem twice. Lineage from feature to model to decision does the same job for data that a design document does for code, with the advantage that it is generated rather than written and therefore cannot quietly fail to exist. Our Platform work starts there, with the question of what a team is allowed to build its own version of and what it must call, because that boundary is the actual architecture.
The current wave of cautious language-model pilots makes the discipline more urgent, not less. A tool that lets a small group produce working software quickly is also a machine for generating variants at speed. If four groups each stand one up against the same data, the review above is being rehearsed with a shorter runway.
Adding people to a build without an architecture is how you get several builds. The headcount line moves, the burn-down looks briefly better, and the thing you are actually accumulating is the cost of reconciling work that should never have diverged.
