The constraint is not your skill, it is their ceiling
When you are the only engineer on a customer site, the natural instinct is to build at the level you are capable of, because you are the one building it and there is nobody to slow you down. That instinct produces systems that work perfectly for the last week of the engagement and rot within two quarters. The relevant capability is not yours. It is that of the two or three people who will be on call for this after you have flown home, and who did not write any of it.
That reframes the design question. Instead of asking what the best architecture for this problem is, you are asking what the best architecture is that this specific team can operate, extend and debug at 2am without you. Those are different questions and they frequently have different answers. A message-driven pipeline with an event store may be the right shape for the problem in the abstract, and the wrong shape for a customer whose entire operational competence is a scheduled job pulling from a relational database, because the first time it develops a consumer lag nobody will know what they are looking at.
Audit before you choose
The first substantive work of an embedded engagement is not writing code, it is finding out what the organisation already runs and already understands. Which languages appear in the repositories their team actually maintains, not the ones in a strategy document. What their deployment mechanism genuinely is, including the manual steps nobody admits to. What monitoring exists and who looks at it. Whether they have anyone who has operated the database you were about to introduce.
The output of that audit is a short list of things you will build with, and it should be shorter than you would like. A useful discipline is to allow yourself a fixed, small budget of new concepts — perhaps one — for the entire engagement. If the problem genuinely requires a queue and they have never run one, spend the budget there and do everything else with tools they already own. If you spend it on a service mesh, a new language and a new datastore, you have not delivered a system, you have delivered a training obligation nobody funded.
| Their reality | Tempting choice | What you build instead |
|---|
| Team runs Python scripts and Postgres | Event-streamed pipeline with a new broker | Scheduled Python job, staging table, idempotent reruns |
| One engineer, no container experience | Kubernetes deployment with Helm charts | Single VM, systemd unit, deploy script in the repo |
| No monitoring beyond email alerts | Full metrics stack with dashboards | Health-check row in a table their alerting already reads |
None of the right-hand column is what you would build for yourself. All of it is still running eighteen months later, which is the only test that matters.
Where you should refuse to simplify
The reasoning above has a limit, and candidates who apply it uncritically fail from the other direction. Some complexity belongs to the problem, and removing it does not simplify anything, it just moves the difficulty from your code into their operating life. Correctness constraints are the clearest case: if the source data can arrive twice, the job must be idempotent, and there is no simpler version of that which is also correct. Reconciliation, retry semantics, an audit trail of what was computed from which input — these look like sophistication and are actually the things that stop the receiving team from being asked questions they cannot answer.
The distinction to hold onto is between complexity that is visible and complexity that is hidden. A pipeline with three explicit stages and a staging table between each is more code than one clever transformation, and enormously easier to inherit, because a maintainer can look at the intermediate state and see where it went wrong. Prefer the design with more surface and less depth. Cleverness compresses; inheritance needs the opposite.
Build the handover into the work, not after it
Handover treated as a phase at the end produces a document nobody reads. Handover treated as a constraint throughout produces a system that does not need much of a document. Practically, that means the customer's engineers should have written parts of this, or at minimum reviewed it, from early on — even where doing it yourself would be faster, which it always is. It means the deployment path must be one they have executed, with you watching, before you leave. And it means the failure modes should have been rehearsed: break something deliberately in a controlled window and let their engineer find and fix it while you are still in the building.
A specific test worth applying to yourself in the final fortnight: if you were unreachable and the nightly load failed with a schema change upstream, what exactly would their on-call person do? If the honest answer involves reading your source code to work out where the data goes, you have more work to do, and it is not documentation work.
The clever-abstraction tax
The most common way a solo embedded engineer damages a customer is by building the abstraction that made their own work faster. A configuration-driven framework, a small DSL for defining transformations, a generic loader that handles any source shape — each of these genuinely pays for itself across the several similar things you had to build in twelve weeks. Then you leave, and the receiving team faces a request to add one field. In a plain, repetitive implementation that is a five-minute change they can make by copying the line above. In your framework it requires understanding the framework, and the framework has one author and no users.
The uncomfortable conclusion is that duplication is sometimes the correct choice on an embedded engagement, because it is the form of code that survives being maintained by people who do not have the model in their heads. Write the same twenty lines three times. If they later want to factor it out, they will do it with their own understanding of which parts vary, which is exactly the understanding you lacked.
The system you should build is the most boring one that meets the requirement, because the maintainer is not you and the deadline you are designing against is not the end of the engagement.
© 2026 Preptima. Originally published at preptima.com (opens in a new tab).