Leaving a codebase behind

What you actually deliver when you leave a project — and why most handoffs fail

Today I closed a chapter. After months of work, I'm moving on from a project I care about. The kind of project that starts as "we need an app" and turns into an archaeological dig through thirty-something automation workflows, a frontend held together by browser automation because the ERP had no API, and a team that meant well but built an amalgam — each piece solving an immediate problem, none of them working together.

I'm not here to trash the project. The company had real customers, real revenue, and a team that showed up every day. But the architecture was what happens when "move fast" becomes a permanent strategy. No single person designed this system. It grew like moss — filling gaps, adapting to pressure, never getting pulled out by the roots because who has time for that when the next feature is already late?

The situation I walked into

When I joined, the codebase looked like this:

  • Thirty-plus n8n workflows with names like "Rotina Sync" and "Rescisão - Cálculo e Cobrança" — each one a tangled graph of nodes that nobody could fully explain. One workflow had 194 nodes. Another ran every 30 minutes and had been failing consecutively for days. Nobody noticed because there was no alerting, and the failure was silent — it just didn't sync the data it was supposed to sync.
  • A browser automation layer that treated Puppeteer as a write API. To register a financial transaction in the ERP, a headless browser would open the ERP's web interface, fill in forms, click buttons, and submit. It took 3-4 minutes per operation. If the UI changed, it broke silently. Chrome consumed 200-500MB of RAM competing with the automation tool on the same Docker container.
  • Data in three places with no transactionality. The same customer record lived in the ERP, the payment provider, and a database mirror. No transactions across them. No rollback. When two workflows ran simultaneously, they created duplicate customers. The team had added a "remove duplicates" node — which meant it had already happened enough times to warrant fixing, but not enough times to fix the root cause.
  • Every feature was a separate tool. Authentication here. Payments there. Digital signatures over there. SMS verification from yet another provider. Thirty-something integrations, each with its own error handling (or lack thereof), its own data model (or approximation of one), and its own silent failure mode.

The system worked. Customers rented vehicles. Payments were processed. Contracts were signed. But it worked the way a bridge made of duct tape works — you can drive across it, but you don't want to look down, and you definitely don't want to be the one maintaining it.

What I built

Instead of patching the next workflow, I went back to fundamentals.

A real domain model

Seven entities with explicit state machines. A Person goes from prospect → verified → active → suspended → inactive. A Contract goes from draft → pending signature → active → completed, cancelled, or defaulted. Each transition has rules. Each state has meaning. No more "what does status 3 mean again?" — the code tells you.

This wasn't academic exercise. The legacy system had hard-coded step tracking (passo_atual in the database — "current step" as an integer). Our domain model encodes the same business logic but in a way that a developer can read, test, and extend without reverse-engineering a spreadsheet.

Clean architecture, for real

Ports and adapters. Hexagonal structure. Bounded contexts. Not the "we'll refactor later" kind — the real kind, where the domain has zero dependencies on infrastructure and the adapters can be swapped without touching business logic.

The coupon module was the first bounded context we shipped. It has a Bitrix24 adapter that talks to the CRM, use cases for creating, validating, and redeeming coupons, and a repository interface that means tomorrow we could swap Bitrix24 for something else without touching a single line of domain code. That's not theoretical. It's running in production.

AI-oriented development skills

I didn't just leave them code. I left them a way to keep building.

The project has structured skills — documented, repeatable workflows that an AI agent can pick up and execute. Each skill encodes not just what to do but why, what the constraints are, what the domain rules mean. When someone (or something) asks "how do I add a new payment provider?", the answer isn't buried in a 194-node workflow. It's a skill document that says: implement this port, write this adapter, run these tests.

Context preservation at the architecture level. When someone leaves a project, the code keeps working. What leaves with them is the understanding of why the code works that way. The skills externalize that understanding.

A functional architecture guide

Not a 50-page PDF that nobody will read. A living document that describes the system as it actually is: what each bounded context owns, how they communicate, what the integration points are, where the migration path leads. Written for the next developer — human or AI — who needs to understand the system without archaeological excavation.

What "leaving well" actually means

Most exit stories are about the dramatic moments — the confrontation, the last day, the emotional goodbye. This one is about something less glamorous but more important: what you leave behind.

I could have left a pull request. Most people do. "Here's what I was working on, good luck." Instead, I left:

  1. A working product that the end client has approved. Not a prototype. Not a proof of concept. Something that runs, handles real transactions, and passes acceptance tests.
  2. A domain model that encodes the business rules correctly. Not my interpretation of the rules — the rules as they actually work, validated against production data and edge cases.
  3. A migration path from the patchwork to the new architecture. You don't stop the train. You replace the tracks underneath it, one section at a time. The coupon module proved this works. The rental module is ready for the same treatment.
  4. AI-optimized documentation — skills, not just comments. Context, not just code. Because the next person working on this system might not be a human reading a README. It might be an agent following a structured workflow. Either way, they need to know the same things.
  5. Good relationships. Everyone on the team has my number. They can reach out personally or professionally. I didn't burn a bridge on my way out. I built one.

Why this matters

The tech industry treats departure like a checklist. Knowledge transfer sessions that nobody records. Handoff documents that nobody reads. Surprise when the next person takes three months to figure out what the previous person built.

More documentation won't fix this. Better architecture will. Architecture that explains itself. Domain models that encode intent. Bounded contexts with clear boundaries. Skills that make the implicit explicit.

I'm moving on to my next project. But the code I left behind doesn't need me to explain it. It speaks for itself.

← Back to Blog