The software as craft movement is built on a premise of customer focus and personal responsibility. Let the customer decide the priorities, but be absolutely professional in delivering. Design the product with care, test everything, and use all the best practices, and you're home free. The implication is that, if that really doesn't work out, you're just not a professional.

I find this premise deeply unsatisfying.

It can work really well when the team uses clean practices from day one. But how many of us have the good fortune even to be around on day one? Most of the time we inherit an existing application from other engineers and -- let's face it -- the engineering standards we encounter are seldom up to par with our expectations. When it's a matter of some missing tests or messy code, we can usually deal with it with legacy code techniques or refactoring in the context of feature delivery. But problems are all too often far deeper than that. Maybe the system architecture is broken, or the data model is unsuitable, or the application doesn't even have well-defined build and deployment processes. These things could take a fully staffed team weeks or months to fix.

It can be difficult to get stakeholders on board with the necessary investments to fix such fundamental problems. Often the modus operandi is muddle through: live with the bad design and somehow continue to deliver the features stakeholders want. After all, nearly always muddling through for any one particular feature costs less than fixing the underlying mess. But, as we all know, this is a mirage: not only does this make an engineer's life miserable, but it inevitably leads to piling more and more technical debt onto the application -- exactly the opposite of what a professional software engineer should strive to do.

Realistically, to fix such problems, a team needs to devote so many resources to improving the architecture that their capacity to deliver features the stakeholders want is severely limited. So is a professional software engineer supposed to proceed?

Saying No

One answer may well be to say "no" to the whole project. That is, before even agreeing to work on a product or to join a team, carefully inspect their architecture and practices and see whether it is realistic to do the kind of work expected by the stakeholders. Reject any project which doesn't meet the bar.

Were life only so simple! There are a couple of problems with such an approach:

  • One does not always have access to all the required information before joining. This is particularly true when joining a new company as an internal software engineer. Rarely will the company let you see their code before you sign the contract.
  • If one filters out all projects with severe architectural problems, one will quickly find that the selection of acceptable projects is practically empty.

Of course, this approach also doesn't help one if one is already in such a project.

Kinds of architectural debt

I use the term architectural debt to describe problems whose scope is large enough that one cannot realistically solve them in the context of implementing a single feature or resolving a single bug. As opposed to technical debt, which can be paid down tactically "as one goes", paying down architectural debt requires planning and strategy. Because of that, one can't get around some degree of negotiation and alignment with product stakeholders. How that works depends on the alignment of the stakeholders' interests with those of the engineers.

Here's where a senior software engineer can get some extra "soft" skills to really stand out. Negotiation is not rocket science but it requires understanding where our natural human instincts (think fight or flight) can become a real hindrance. Successful negotiation requires recognizing our own (often poor) instincts and getting ahead of them. To learn more about this, I heartily recommend William Ury et al.'s seminal trilogy Getting to Yes, Getting Past No, and The Power of a Positive No.

I broadly define three types of architectural debt: time bombs, tar pits, and booby traps. These have different dynamics when aligning with stakeholders, as we will see below.

Time bombs

We speak of a time bomb when the product as it currently stands, or some important aspect of it, will stop working at some point in the future if no changes are made. Examples of this are:

  • A critical dependency of the product, such as an API, is to be turned down.
  • The load on the application is inexorably increasing and, after a certain amount of time, the product will no longer be able to scale to handle it.

In this case, the primary interest in resolving the problem lies with the stakeholders. They are, after all, interested in having a working product. Engineers certainly do not want to see their product stop working either. Thus there is a broad alignment of interests. Negotiating the work to eliminate the problem should be unproblematic, assuming both sides are acting in good faith.

Here the onus lies on the engineers to start the process. They have a professional ethical responsibility to ensure that the stakeholders are informed of potential time bombs as soon as they become apparent. Stakeholders should then be able to prioritize product work appropriately to defuse the time bomb before problems arise, assuming that is still possible.

Tar pits

A tar pit is an aspect of the application which greatly and unnecessarily increases engineering effort. Examples may include:

  • Lack of testability,
  • Convoluted design,
  • Slow or unreliable build and test processes.

In this case, the interests of the engineers are largely aligned with those of the stakeholders. Such problems make working on the product frustrating but also greatly increase development costs. In principle, stakeholders and engineers should be able to agree to prioritize the work to fix these problems if the engineers can clearly make a case that doing so reduces costs more than the required investment.

Of course, these costs can be hard to measure, especially if one considers the true cost: engineers don't want to work in tar pits, so when faced with such problems, they tend to leave. Worse yet, it's usually the best -- the most creative, innovative, and efficient -- engineers who leave. It's worth making stakeholders aware of this, but be careful that it come across as a warning and not a threat.

Booby Traps

A booby trap is a too easily made potential mistake with real costs. The canonical example of this is missing test coverage: a well-meaning engineer may unknowingly break production code with no safeguard between their change and the costs which follow. There are many other examples.

This is the most problematic from the perspective of negotiation, since the primary interest lies with the engineer in solving the problem. The stakeholder has only a secondary interest in avoiding the costs of such mistakes. Unenlightened stakeholders may, however, simply argue that the danger is the "engineer's problem", that it's "their responsibility not to make mistakes" and that they will not invest in measures to protect engineers from such problems.

Here I must be blunt: a professional software engineer has an ethical obligation to themselves, their colleagues, and ultimately their stakeholders to eliminate booby traps when they find them. This requirement, in my view, supersedes the stakeholder's prerogative to prioritize product work. Indeed, the stakeholder is not in a position to evaluate the priority of such work, since they (normally) do not have the necessary technical background. The team should therefore plan and schedule such work itself and reduce its available capacity for product development as appropriate.

Conclusion

Senior software engineers have a professional obligation to look after the health of the products they developing. There is a natural tension of interests between the technical leadership of a team and the product stakeholders. A good engineer uses negotiation techniques to make sure the technical point of view is respected, even if it means that part of the team's engineering capacity is not immediately available to do what product stakeholders would prefer. That's the key to a healthy product: technical and product leadership negotiating as equal partners.

Category:  Opinion  |  Tags:  software engineering   strategy