Practice
Architectural approaches are usually choices made under particular pressures, with benefits and costs.
These are choices made in systems that are running: what each one was responding to, what it bought, and what it still costs. Under different pressures the same questions would have had different answers.
Make the dependency visible to the compiler
- Choice
- Each service ships a typed client that the backend compiles in, instead of hand-written types on both sides.
- Reason
- The backend depends on another service's field names whether or not anything writes that down.
- Benefit
- A changed contract stops the build rather than surfacing in front of someone.
- Cost
- Builds have to be coordinated — and one seam, where copied enums are bridged by number, still slips through.
One place where the product is composed
- Choice
- Frontends talk only to their product's backend, and subsystems never call each other for product meaning.
- Reason
- Every extra caller is another place where authorisation and the shape of the product get decided.
- Benefit
- When the product's shape is wrong, there is one file to open.
- Cost
- That backend gathers weight, and needs a stated rule to stop it absorbing work that belongs elsewhere.
Ids across a boundary, not foreign keys
- Choice
- One service owns each piece of data; the others hold an id and ask.
- Reason
- Services with separate databases cannot share integrity constraints anyway.
- Benefit
- Each service keeps its own deployment, backup and recovery policy.
- Cost
- Joins that used to be free become calls that have to be made and composed.
The same shape inside every service
- Choice
- Controller, facade, business services, repository — laid out identically in every service.
- Reason
- Finding what is genuinely the same across services costs less than letting each one invent its own arrangement.
- Benefit
- Someone who learns one service can find their way around all of them.
- Cost
- For a genuinely small service it is more structure than the work needs.
What produces a number does not get to confirm it
- Choice
- A separate verifier recomputes each claim from the source figures, sharing no code with whatever produced it.
- Reason
- Code checked by itself demonstrates that it agrees with itself.
- Benefit
- It caught a routine that silently failed to converge and inflated a value about 4.75× — 215 contradictions, then none.
- Cost
- Two implementations of the same arithmetic, kept apart on purpose.
Different data, different care
- Choice
- One system regenerates its schema and drops its database; the other refuses to start without a migration.
- Reason
- One holds simulation output that can be rebuilt in a command; the other holds paid data and real user state.
- Benefit
- Neither pays for ceremony that its data doesn't need.
- Cost
- The regenerating one exports a manual step to production every time a column is added.
Durable anchors are marked gone, not removed
- Choice
- Users, companies and sessions are tombstoned rather than deleted.
- Reason
- History, audit trails and references hang off them long after they stop being active.
- Benefit
- Removing something never orphans the record of what it did.
- Cost
- Every read has to filter, and forgetting once shows a deleted thing to someone.
Spending time discovering what is genuinely the same can reduce the number of variants that need to be built.
That is the opinion underneath several of the choices above, and it is the one I hold most strongly — not because sameness is a virtue, but because two things that were never actually the same are expensive to merge, and two things that always were are expensive to keep apart. Telling those cases apart takes conversation before it takes code.
How the work actually goes.
I am at my most useful when I can question someone who genuinely understands the product — not to collect requirements, but to keep asking until the shape underneath them shows itself. A list of features often contains fewer real mechanisms than it appears to. Requirements that arrive as separate items are sometimes one mechanism seen from different sides, and it is much cheaper to find that out before three of them are built.
That questioning is the part I would want to protect in any engagement. It can feel unproductive while it is happening, because nothing is being built yet. It also needs someone on the other side who can say “no, those two really are different, and here is why” — which is why it is a conversation and not an interview.
Then the second half: whatever we agree gets externalised. System maps, class diagrams, admin views that show the running state. Not documentation written after the fact, but surfaces the system produces about itself. Their purpose is to give the product owner and me the same object to point at — something we can both read, disagree about, and correct. A drawing that only I can read is a drawing that only I can check.
A shared surface

The offer.
Hiring a senior developer is slow, expensive, and hard to reverse. You commit before you have seen the work, and the real test — how someone reasons about your system, not how they perform in an interview — only starts after the contract is signed. This inverts that.
- One paid week — €800. You give me one meaningful backend task, repo access, and enough context. I spend the week understanding the part of the system it touches and delivering a concrete result. Not a tiny isolated ticket — a real piece of work where understanding the system is the point.
- If it works, we continue — around €3,000 / month. Remote, through my own EU company, month to month, no lock-in. Senior European backend capacity at a fraction of the cost and commitment of a local hire.
- Low risk by construction. You are not making a hiring decision under uncertainty. You get one meaningful result first, and judge by that. If it is not useful, there is no obligation and nothing to unwind.