Back to Insights

Building the Tracks Before the Train

A foundation should earn its keep before you know whether the company will scale.

6 min readBy The Bushido Collective
StrategyStartupTechnical LeadershipInfrastructure
Share:LinkedInX
If a billing change requires untangling account rules scattered through the login code, the feature estimate is carrying work nobody asked to buy. You still have to decide how much of that untangling to fund while the customer waits for the billing change.

Someone should have built a better foundation, perhaps. Or the original design was a reasonable way to test a business that might never find customers. The size of today’s rewrite request can’t settle whether yesterday’s decision was wrong.

The distinction worth investigating is which decisions kept getting remade. A product’s account rules may genuinely change as the business learns. The way an engineer runs its tests shouldn’t need reinventing with each feature. Early foundations earn their keep when they let the team change the former without repeatedly sorting out the latter.

A foundation small enough to use

In GitHub’s 2015 account of its shared development scripts, Jon Maddox described a convention that worked across projects using different languages. script/test ran a project’s tests locally. The script used by GitHub’s automated build servers, script/cibuild, called that same test script.

The names and responsibilities were consistent; each project could choose the appropriate implementation. Contributors could ask for a test run through a familiar command without first learning the project’s particular tools. The automated build reused that entry point rather than maintaining a separate way to invoke the tests.

Maddox didn’t publish a measured time saving. A familiar command also says nothing about whether the tests catch the failures that matter. The useful inference for a small team is narrower: standardize an operation people already repeat, while leaving room for different implementations. You can adopt that principle inside an existing application. You don’t need GitHub’s scale or a new internal platform.

The decision tax

Return to the billing example. Suppose account checks have been copied into each place that updates billing details. Before changing a rule, an engineer has to find the copies and determine whether their differences are deliberate. A reviewer then has to establish whether the change covered the right places. The feature estimate contains both jobs, even if the ticket only names the customer’s request.

That repeated reconstruction is a decision tax. Each additional copy is another place to inspect, even when traffic and headcount stay flat.

If those callers implement the same rule, a shared account-checking function gives the engineer a defined place to inspect it. That still leaves the hard question of what the rule ought to be. Once the callers have moved to it, the engineer no longer has to choose which copy expresses the rule correctly.

Suppose, though, that one path checks whether a customer can edit payment details and another checks whether an automated job can charge the account. Both use the account record, but they answer different questions. If a common function treats these permissions as interchangeable, changing the charge policy can change what the customer is allowed to do. Similar-looking code hasn’t established a common rule.

The useful parallel with GitHub’s scripts is the stable way to request an operation, with implementation details kept behind it. GitHub left each project free to implement its own tests. In the billing code, the boundary has to preserve genuine differences too. A document declaring a standard won’t reconcile the copies already scattered through the application.

This also gives conventions a limit. If a shared function or script needs a new exception for each use, investigate whether it joined things that should differ. Maintaining the convention is work too. Its value depends on how much repeated work it actually removes.

Payback without a growth story

A founder has a sound objection to building for the future: the company may run out of money before that future arrives. Looking only at successful companies won’t answer it. Their foundations might have helped them survive; surviving also gave them opportunities to improve those foundations. A success story alone cannot separate the two.

That uncertainty changes which work deserves to come first. A test checking that one customer’s account can’t read another customer’s invoice can catch a regression in a rule the product already needs. A repeatable release command replaces steps someone otherwise has to remember on the next release. Recording which billing operation failed, with a request identifier to trace it, gives the team evidence for investigating that failure.

Each has an immediate use, but the implementation still has to be proportionate. If the team can already release repeatably using its hosting provider’s tools, building a custom deployment service needs a separate justification. A platform for independently deploying teams requires evidence that those teams need independent releases. Future headcount on a fundraising slide supplies neither requirement nor payoff.

The foundation also has to compete with delivering the customer change sooner. Compare a safe change in the existing code with the same change after consolidating the account checks. The latter price includes moving callers and checking that their behavior survives, not just writing the shared function. If recovering that extra effort depends on features nobody has agreed to build, the justification still rests on a growth story.

Martin Fowler’s 2014 essay on sacrificial architecture makes room for deliberately replacing early software. He argues that code intended for replacement can still deliver value, while good module boundaries help make replacement possible. The early design can have a limited life without every feature becoming an exercise in finding where its rules live.

When the bill has arrived

If you’re already facing that billing estimate, ask the engineer to separate the business rule that’s changing from the repeated work required to reach it. Can the affected callers use a common account check, and which differences must remain? Tests of the new shared function leave an important question unanswered: do the billing paths actually use it? Exercising those paths can expose a caller still applying an old rule.

If the safe local fix is smaller and there’s no named next change that benefits from consolidation, leaving the copies in place can be defensible. Tests showing which permissions each path requires still record something useful for the next change.

Where replacement really is necessary, Fowler’s strangler fig approach describes moving behavior out of an existing system in pieces while old and new code coexist. He also describes temporary code that becomes unnecessary once the migration is complete. In the billing example, if both versions can change a billing record, someone has to decide which version handles each update during the transition. Delivering a working piece before replacing the rest can be valuable, but this coordination work belongs in the price.

You still have a feature to approve. Ask to see the route through the code today, then the route the proposed foundation would leave behind. If the next billing change still requires discovering and reconciling the same scattered rules, the expensive part of the work is still there.

Want this looked at in your business?

Start with the rough map: thirty minutes, owner to owner, and a written report on where AI pays off for you and what it's worth. It's free, and if you don't need us the report says so.

Get your rough map, free

Not ready to talk? Stay sharp anyway.

We send insights like this to technical leaders every week or two. The thinking we bring to our engagements, no fluff, no spam.

Keep reading

Share:LinkedInX