<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mansur Fattakhov</title>
    <description>The latest articles on DEV Community by Mansur Fattakhov (@fattakhov).</description>
    <link>https://dev.to/fattakhov</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4122926%2Fd92a7478-2662-4406-9360-c4c7b3797890.jpg</url>
      <title>DEV Community: Mansur Fattakhov</title>
      <link>https://dev.to/fattakhov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fattakhov"/>
    <language>en</language>
    <item>
      <title>How to Handle Incidents in a Small Team</title>
      <dc:creator>Mansur Fattakhov</dc:creator>
      <pubDate>Sat, 14 Nov 2026 18:49:27 +0000</pubDate>
      <link>https://dev.to/fattakhov/how-to-handle-incidents-in-a-small-team-5d7n</link>
      <guid>https://dev.to/fattakhov/how-to-handle-incidents-in-a-small-team-5d7n</guid>
      <description>&lt;p&gt;As long as the service is running, it's easy for a small team to assume it doesn't need an incident process. Everyone knows everyone else, you can get hold of anyone, and if something breaks, you'll figure it out in chat. That agreement gets its first real test when one person is rolling back a release, another is changing the configuration, and someone in chat is asking when things will work again. Everyone is doing something useful. Nobody has the whole picture.&lt;/p&gt;

&lt;p&gt;My &lt;a href="https://mind.mansur.expert/en/alerts-worth-waking-up-for/" rel="noopener noreferrer"&gt;article on alerts&lt;/a&gt; was about noticing a problem. This is what comes next: the alert arrives, somebody opens a laptop, and then what? In a small team, one or two people should be able to put a process in place without a dedicated department or a form that takes half an hour to fill in. You need a few agreements that help you restore service and leave you with something to show for the outage besides exhaustion.&lt;/p&gt;

&lt;h2&gt;
  
  
  A shared channel: who's taking what?
&lt;/h2&gt;

&lt;p&gt;My starting point is a shared incident channel. When a report comes in, that's where we say who's picking up which piece. Everyone needs to be able to see it: one person is checking application errors, another is looking at infrastructure, a third is finding out which users are affected. A read receipt doesn't tell you who's actually working on the problem.&lt;/p&gt;

&lt;p&gt;For a major incident, we open a dedicated channel, for example &lt;code&gt;inc-2026-10-06-http-outage&lt;/code&gt; in Slack. The date and short description tell you which outage it is and make the discussion easy to find later. We leave a link in the shared channel and move the conversation to the incident channel. That's where we collect checks, results, and decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get on a quick call to divide the work and assess the risks
&lt;/h2&gt;

&lt;p&gt;When an incident is serious, it helps to get on a call straight away. In those first few minutes, we need a common picture of what's broken, how many users are affected, and what could happen if the problem continues. Current impact and potential risk can be very different. Errors might still be rare, but the incident is hardly minor if data could be lost or the failure could spread.&lt;/p&gt;

&lt;p&gt;On the same call, we agree on who does what and who keeps track of the overall situation. People need to know what the others are doing. Otherwise, someone may change a setting just as someone else rolls back a release, leaving us to guess which action helped and what else changed in the meantime. You don't need an elaborate set of roles. Divide the work explicitly and talk through the next few steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everyone records their own actions and results
&lt;/h2&gt;

&lt;p&gt;It's worth posting a short account of the call in the incident channel: what we know about the impact, what each person is working on, what we've decided to try, and when we'll post an update. Anyone joining later can catch up without asking for a recap. Once the service is back, there's already material for the postmortem.&lt;/p&gt;

&lt;p&gt;As the work progresses, it helps if each person notes the time and what they did: when they took the task, what they started investigating or changing, and what happened. Short personal notes are fine; they can go into a shared timeline after the incident. This matters especially when several people work in parallel. Even after a brief outage, it's surprisingly hard to reconstruct the order of events from memory.&lt;/p&gt;

&lt;p&gt;If people are already talking in the incident channel, they can post progress there. Each message already has a name and a timestamp. An engineer might write: “I'll take restoring database access. First I'll check whether the database is reachable from the internal network.” Then they come back with the result: “I can reach the database from the internal network, but connections from the application nodes aren't getting through. I'm checking network access from those nodes.” This is an illustrative entry. What matters is the link between what the engineer set out to check and what they found, whatever the eventual cause.&lt;/p&gt;

&lt;p&gt;These messages serve two purposes. The team can see where things stand, what's been checked, and who's working on what. The postmortem gets a sequence of events with names and times. Even if someone keeps private notes, the key findings should still go back into the channel. After recovery, we can bring the notes and messages together in a single timeline. Keep the checks that led nowhere, too: they explain what we knew at the time and why the next step made sense.&lt;/p&gt;

&lt;p&gt;In the working record, it helps to distinguish observations from assumptions: when we noticed the problem, what users couldn't do, what we tried, and what happened. “The errors stopped after the rollback” is an observation. “The latest release definitely caused the errors” is still a hypothesis. Write both with equal certainty and the postmortem will start with people defending the first guess that paid off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Restore service first
&lt;/h2&gt;

&lt;p&gt;During an outage it's easy to get absorbed in the explanation: why did this happen, which line is responsible, who changed the setting? What users need right now is a working service. Sometimes rolling back, disabling the failing path, or temporarily limiting a feature is enough to give them that. We'll still need to find the cause. Recovery and investigation solve different problems and can move at different speeds.&lt;/p&gt;

&lt;p&gt;Metrics tell an engineer how the system responded to a fix: whether errors dropped, latency returned to normal, and requests are being processed again. That's valuable evidence. It's even better if we can repeat the user journey that actually broke. If the team has testers, they can run through it and check the outcome. If the error only affects a particular user and we can't reproduce it ourselves, we can get in touch and ask them to try again.&lt;/p&gt;

&lt;p&gt;This is where the project manager and the rest of the team can help. They can run checks, contact the user, and bring the answer back to the channel. Communication and verification can happen while the engineer works on recovery. The shared record then shows both what the metrics say and what the user experienced. If the graphs are back to normal and the user hasn't replied, say exactly that: we see technical recovery and we're still waiting for confirmation on this particular case.&lt;/p&gt;

&lt;p&gt;When choosing the next step, I'd look first at how easy it would be to undo. Disabling a new processing path may be safer than reworking shared configuration on the fly. A rollback isn't harmless by default, either: if the release has already changed the data, the old version may no longer be able to handle it. Before acting, take a moment to say what you expect to see and what you'll do if things get worse. That gives everyone the same way to judge the result of the recovery step you're about to try.&lt;/p&gt;

&lt;p&gt;Time permitting, save the evidence that the change will erase: a section of the logs, when the errors started, the running version, settings that changed, or the state of a queue. Don't delay recovery for the sake of a perfect archive. Someone who isn't applying the fix can gather what's available and leave links in the channel. Sensitive data stays in the storage intended for it rather than spreading through screenshots in the shared chat. After a restart, a process may look perfectly healthy and tell you nothing about what was wrong ten minutes ago.&lt;/p&gt;

&lt;h2&gt;
  
  
  A blameless postmortem with clear ownership of the fixes
&lt;/h2&gt;

&lt;p&gt;Choosing not to look for someone to blame doesn't let everyone off the hook once the meeting is over. It's more useful to ask why an action seemed reasonable with the information available, which safeguard was missing, and what will now change. You can't verify “be more careful.” You can verify a task with an owner, a deadline, and a clear definition of done.&lt;/p&gt;

&lt;p&gt;By the time we write the postmortem, our action items are already links to issues in the tracker, each with an assignee and a due date. “Add a database connectivity check” on its own doesn't tell you who will do it or when. The issue should make the intended change and its acceptance criteria clear. From the postmortem, anyone can jump straight to the discussion and current status.&lt;/p&gt;

&lt;p&gt;Several actions may be necessary: fix the cause, add a missing check, clarify the recovery procedure. Each gets its own issue, owner, and due date. That's how a postmortem becomes part of the regular rhythm I described in the &lt;a href="https://mind.mansur.expert/en/processes-a-living-rhythm-with-formal-points/" rel="noopener noreferrer"&gt;processes track&lt;/a&gt;. The team's commitments make it into its work plan, where they stay visible long after the incident stops feeling urgent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping the outcome alive a week later
&lt;/h2&gt;

&lt;p&gt;The next day, the team is back to its usual work. The urgent messages scroll out of view, the incident channel goes quiet, and the promised fix has to compete with the next release. Even a good postmortem can come to nothing here: everyone understood what happened, but a month later nobody remembers what became of “we should do something about that.”&lt;/p&gt;

&lt;p&gt;I'd keep a short entry for each incident in a shared register: the date, the impact users saw, links to the channel, timeline, and postmortem, then the follow-up issues and their current status. A spreadsheet or a page of links is enough. There's no need to copy the whole postmortem; the point is to find a particular incident and spot unfinished work.&lt;/p&gt;

&lt;p&gt;Revisit those issues during normal team planning. If a deadline has passed, it's more useful to understand why and agree on what to do next than to move the date. Sometimes the original fix turns out to be too big; sometimes another change makes it unnecessary. Leave that decision on the issue, too: why it was replaced or closed and what risk remains. A green incident status means the service is back. It doesn't mean every promised improvement is in place.&lt;/p&gt;

&lt;p&gt;Before the next postmortem, look for similar incidents in the register. You might find that you've seen this outage before, the follow-up issue is still open, and the team has just spent the first twenty minutes running the same check again. At that point the useful question practically asks itself: what's stopping us from finishing the fix?&lt;/p&gt;

&lt;p&gt;This simple setup has limits. If several incidents run at once, multiple teams get involved, or ownership regularly gets lost in handoffs, agreements in a single chat won't be enough. You'll need explicit arrangements for coordination, on-call coverage, and handoffs. Add that complexity when you've seen the problem it will solve. To start with, it's enough that everyone can tell who's restoring service, who's talking to users, and where to find the shared record.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should be left after an incident?
&lt;/h2&gt;

&lt;p&gt;Before everyone goes back to their day, I'd check five things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's clear who picked up the initial report and how the work was divided.&lt;/li&gt;
&lt;li&gt;Actions and results are in one place, with names and timestamps.&lt;/li&gt;
&lt;li&gt;Metrics and a check of the affected user journey confirm recovery; any remaining uncertainty is recorded.&lt;/li&gt;
&lt;li&gt;The postmortem links to issues with assignees, deadlines, and acceptance criteria.&lt;/li&gt;
&lt;li&gt;The issues have a place in the team's work plan, where their status will come up again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can tell an incident process is working from a small moment: somebody joins the conversation and quickly understands what's going on and how they can help. They don't have to piece the story together from three private chats or interrupt the engineer who's restoring database access. And a week later, the team can open the record and see what changed because of the outage. That's worth agreeing on in advance.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>sre</category>
      <category>incidentmanagement</category>
      <category>leadership</category>
    </item>
    <item>
      <title>Automating Support with AI: From a User's Question to a Response</title>
      <dc:creator>Mansur Fattakhov</dc:creator>
      <pubDate>Mon, 09 Nov 2026 08:38:54 +0000</pubDate>
      <link>https://dev.to/fattakhov/automating-support-with-ai-from-a-users-question-to-a-response-2dni</link>
      <guid>https://dev.to/fattakhov/automating-support-with-ai-from-a-users-question-to-a-response-2dni</guid>
      <description>&lt;p&gt;A user asks a question that support has answered before. An agent reads the message, clarifies the details, finds the right instructions, and explains what to do. A little later, another person asks something similar. For them, the problem is new. For the team, it is another trip down a familiar path.&lt;/p&gt;

&lt;p&gt;This is an illustrative scene, but it captures exactly the routine we wanted to automate in our support operation. The goal was practical: answer standard user requests as automatically as possible and reduce support costs in the process. When a request already has a clear procedure, you want a person to step in where their knowledge or authority is actually needed.&lt;/p&gt;

&lt;p&gt;That led to a service connecting language models to support work. Messages arrive from Telegram and Discord, while agents manage conversations in Chatwoot. But the most interesting part turned out to be what happens between the incoming question and the outgoing reply: what knowledge the model receives, what decision it returns, and what the service does with that decision.&lt;/p&gt;

&lt;p&gt;At first, this path did not end with a message to the user at all. The assistant left suggestions in private notes visible to agents. We could see how it handled a request before giving it permission to respond on its own.&lt;/p&gt;

&lt;p&gt;This is the first article in a series on automating support with AI. We will follow a single request from context assembly through an LLM call, result validation, and the choice of a next action. Later parts will look more closely at the knowledge base and conversation history, model tiers and processing costs, human handoff, and protocol failures. Chatwoot and the messaging platforms provide a concrete example; the core decisions also apply to other support systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  The conversation already lives somewhere
&lt;/h2&gt;

&lt;p&gt;Users write wherever it suits them. In our case, that means Telegram or Discord. Agents, meanwhile, need to see the request, its history, and its current state. Chatwoot handles that part.&lt;/p&gt;

&lt;p&gt;The integrations deliver messages and connect them to the right conversation. Processing begins after that: is an automated response allowed right now, what information should go to the model, and what should happen to its result? In autonomous response mode, a Chatwoot agent-bot event triggers this processing. The trial mode starts from the channel integration and saves the result as a private note. These are alternative ways to trigger processing.&lt;/p&gt;

&lt;p&gt;Looking inside an autonomous response gives us this flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Telegram / Discord message → Chatwoot conversation
                                     ↓
                         processing eligibility checks
                                     ↓
                  history + knowledge base + response rules
                                     ↓
                              LLM call at T1
                                     ↓
                       parse result and select action
                           ↙         ↓          ↘
                        reply     next tier    human agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each subsequent tier also returns a result that needs parsing. Sending a message is just one possible outcome. Chatwoot remains the agent's workspace, with the history, request state, and means to continue the conversation already in place.&lt;/p&gt;

&lt;h2&gt;
  
  
  A human agent saw the first result
&lt;/h2&gt;

&lt;p&gt;In shadow mode, the assistant receives the message, the knowledge base, and the available conversation history. It then proposes a response or a decision to hand the request to a person. The result is saved in Chatwoot as a private note. The user cannot see it, and sending a reply remains the agent's responsibility.&lt;/p&gt;

&lt;p&gt;That is how we started. Before allowing the assistant to speak on behalf of support, we gave it a place beside the person already handling the conversation.&lt;/p&gt;

&lt;p&gt;For an engineer, this offers a useful observation point. You can open a request and see what the assistant would have suggested in that context: did it find the right information, understand the question, or start answering where a person was needed? Alongside the result, you can see the processing tier, token usage, and cost, if the provider returned it.&lt;/p&gt;

&lt;p&gt;The presence of a note does not, by itself, mean the answer was good. The agent may not have read it, may have used just one sentence, or may have resolved the issue in a completely different way. To measure suggestion quality, we need to agree separately on how to evaluate it. A count of generated notes is no substitute.&lt;/p&gt;

&lt;p&gt;Still, this starting point has an important practical benefit: we can examine the assistant's work on support requests without turning every generation mistake into a message to the user. Data access restrictions and the terms under which context is sent to a model remain part of the system's design. A private note changes who can see the result; it does not eliminate the external service call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assemble the task before calling the model
&lt;/h2&gt;

&lt;p&gt;A message saying “it isn't working again” rarely tells you exactly what happened. Earlier in the conversation, the user may already have described the problem and the agent may have asked them to run a check. If the model receives only the latest sentence, it has to guess at part of the conversation.&lt;/p&gt;

&lt;p&gt;The service fetches history from Chatwoot. It includes user messages and public replies, while excluding private notes. Among other things, this prevents a trial response from the assistant from automatically returning in the next request as though it had already been part of the conversation with the user.&lt;/p&gt;

&lt;p&gt;The second component is a knowledge base from Confluence. It contains information needed for answers, alongside separate instructions about when the bot should respond and when it should hand the request to an agent. These materials serve different purposes. Knowing a procedure does not mean the assistant has permission to make every decision associated with it.&lt;/p&gt;

&lt;p&gt;In the path examined here, the knowledge base text is loaded and cached inside the service process. Connecting Confluence does not, on its own, turn the system into vector search: what matters is which data actually enters the request. History length, knowledge base completeness, and update frequency affect both answer quality and the amount of context you pay for.&lt;/p&gt;

&lt;p&gt;That is why I would also begin work on such an assistant by looking at our own documentation: does the team have a sufficiently clear answer to a typical request? If the instructions contradict themselves, the model may faithfully restate that contradiction. A more expensive tier cannot create a missing product rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  An LLM call must end in an actionable decision
&lt;/h2&gt;

&lt;p&gt;For autonomous operation, a string containing well-written text is not enough. The service needs to know what to do next. The model's result therefore contains an action and the data needed to carry it out.&lt;/p&gt;

&lt;p&gt;The code provides four options:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Decision&lt;/th&gt;
&lt;th&gt;Service action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reply to the user&lt;/td&gt;
&lt;td&gt;Send the prepared message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hand off to an agent&lt;/td&gt;
&lt;td&gt;Initiate a human handoff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reply and hand off&lt;/td&gt;
&lt;td&gt;Send the message and continue the handoff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request the next tier&lt;/td&gt;
&lt;td&gt;Repeat processing with the next model configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This contract makes behavior inspectable: we can see which command arrived and which handler executed it. A restricted command list does not guarantee the right choice, however. The model can still propose an inappropriate response. Code checks, prompt rules, and quality evaluation address different parts of that problem.&lt;/p&gt;

&lt;p&gt;There is another step between the API response and command execution: the service must parse the JSON and interpret its fields. This is where a tidy diagram meets everyday integration work. A response may be invalid JSON, or valid JSON with an unexpected structure. Those are different situations, and they are handled differently.&lt;/p&gt;

&lt;p&gt;For broken JSON, there is a separate request to repair the format. It receives the previous call's output, without the knowledge base or conversation history: its job is to restore the structure. If the JSON is readable but the command is expressed differently, the handler tries to recover the action from known fields. This tolerance has a cost: a recovered command can result in a message being sent to the user. Recovery rules therefore deserve as much attention as the normal response path.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to call the next model
&lt;/h2&gt;

&lt;p&gt;There are three processing tiers. Each can be configured with a separate model. It is also possible to use one model with different reasoning depths, which is what the defaults do. It is therefore more useful to think of T1, T2, and T3 as processing configurations than as a mandatory list of three different providers.&lt;/p&gt;

&lt;p&gt;The reasoning behind a cheap-to-expensive strategy is straightforward: a routine question may need only the first call, while additional resources should be spent where they can help. In the current loop, however, a specific action triggers the transition: &lt;code&gt;call_next_model&lt;/code&gt;. A numerical score is recorded, but it does not itself act as a switching threshold. If the first tier decides to answer or call a human agent, the loop ends there. If the third tier requests another tier, the request goes to a person.&lt;/p&gt;

&lt;p&gt;This is an important boundary: there is a limit to how long computation can continue. And needing a human agent does not always mean the model was insufficiently capable. Sometimes the answer requires access or a decision the assistant simply does not have.&lt;/p&gt;

&lt;h2&gt;
  
  
  After handoff, the bot must yield the conversation
&lt;/h2&gt;

&lt;p&gt;When an agent takes over, writing a log entry saying processing has finished is not enough. The user needs to understand what is happening to their request, and the agent needs to know why it was handed over and where to pick up the work.&lt;/p&gt;

&lt;p&gt;In this service, handoff includes notifying the user, adding a private note for the agent, and changing the conversation state. Separate checks take the handoff label and participation of a human agent into account. Their purpose is to stop the assistant from continuing to answer over the person.&lt;/p&gt;

&lt;p&gt;This is now a distributed-system state problem. Events arrive through a webhook, a message can be repeated, and an agent can join between two processing steps. Message deduplication exists at the entry point, but that mechanism alone does not prove that all possible overlaps have been eliminated. Handoff needs to be treated as a scenario of its own, with its own checks.&lt;/p&gt;

&lt;p&gt;This is exactly where the simple picture of “receive text, call a model, send a response” stops explaining the service. We need to account for who is currently handling the conversation and whether a decision made a few seconds ago is still valid.&lt;/p&gt;

&lt;h2&gt;
  
  
  Savings begin with the cost of the whole request
&lt;/h2&gt;

&lt;p&gt;We came to this problem to automate routine work and reduce costs. Looking only at the price of the first request would therefore be insufficient.&lt;/p&gt;

&lt;p&gt;One support request may end after T1. Another may pass through several tiers. A third may need an additional call to repair the response format and still end up with a human agent. The code collects token usage, duration, and available call costs, but human work falls outside the model's meter.&lt;/p&gt;

&lt;p&gt;To understand the outcome, we need a wider view: was the issue resolved, did the user have to contact support again, how long did they wait, and how much work remained for the agent? Cheap generation is useful until the cost of fixing its consequences outweighs it.&lt;/p&gt;

&lt;p&gt;I will not claim a percentage saving in this article; that conclusion requires comparable data. What matters here is showing a design that lets us gradually automate standard requests and examine what happens at each step.&lt;/p&gt;

&lt;p&gt;For us, that path began with a fairly modest result: a note the user never saw. Autonomous replies followed, with a limited set of actions and a way to hand the conversation to a person. Around the model call, the main engineering work gradually revealed itself: context, conversation state, routing, and result validation.&lt;/p&gt;

&lt;p&gt;In the next part, “Context for AI in Support: Knowledge Base, History, and Response Rules,” we will begin with a short user message that is not enough on its own to resolve the issue. We will explore how to assemble the necessary information around it, what to leave out of the request, and why more context does not automatically make support better.&lt;/p&gt;

&lt;p&gt;I also wrote about setting context and task boundaries in &lt;a href="https://mind.mansur.expert/en/giving-a-coding-assistant-a-task-in-an-existing-project/" rel="noopener noreferrer"&gt;“How to Give AI a Task in an Existing Project”&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>architecture</category>
    </item>
    <item>
      <title>How to Learn to Work with a Coding Assistant When Time Is Tight</title>
      <dc:creator>Mansur Fattakhov</dc:creator>
      <pubDate>Sat, 07 Nov 2026 12:49:14 +0000</pubDate>
      <link>https://dev.to/fattakhov/how-to-learn-to-work-with-a-coding-assistant-when-time-is-tight-12e4</link>
      <guid>https://dev.to/fattakhov/how-to-learn-to-work-with-a-coding-assistant-when-time-is-tight-12e4</guid>
      <description>&lt;p&gt;At last, there is a spare half hour in the evening. You could open that walkthrough of a new tool you saved: the tab has been waiting for a week. It will probably contain three more links, unfamiliar terms, and a suggestion to install a few things first. Then morning brings the usual work again: a service bug, a question from a colleague, and a task you promised to finish yesterday.&lt;/p&gt;

&lt;p&gt;On an evening like that, it is easy to decide that getting properly acquainted with a coding assistant requires a holiday. Or at least a quiet week when nobody asks you for anything.&lt;/p&gt;

&lt;p&gt;I would not wait for that week. It has an unfortunate habit of never arriving.&lt;/p&gt;

&lt;p&gt;For a developer, some of the learning material is already sitting in the issue tracker. There are familiar tasks, clear constraints, and people who can tell you whether you delivered what was needed. All that remains is to choose a small piece of work where you can try a new technique and make sense of the result. You do not always need a separate practice project for that.&lt;/p&gt;

&lt;h2&gt;
  
  
  A familiar task gives you something to hold on to
&lt;/h2&gt;

&lt;p&gt;In the previous two articles, we worked through a hypothetical example: an email sent after an order is completed. First, we &lt;a href="https://mind.mansur.expert/en/giving-a-coding-assistant-a-task-in-an-existing-project/" rel="noopener noreferrer"&gt;found our way around the existing project&lt;/a&gt;, then &lt;a href="https://mind.mansur.expert/en/the-assistant-wrote-the-code-is-it-ready-to-ship/" rel="noopener noreferrer"&gt;checked the change before release&lt;/a&gt;. Imagine that work is finished. Another small task appears in the tracker: adjust the data in a similar notification.&lt;/p&gt;

&lt;p&gt;Previously, I would have opened familiar files and started looking for the right place. Now I can hand part of that search to the assistant. Before making the first request, though, it helps to decide exactly what I want to try. Learning to get an explanation I can verify against the code, for instance.&lt;/p&gt;

&lt;p&gt;That is a small enough goal for an ordinary working day. There is no need to learn a new editor, switch models, assemble a toolkit, and invent the perfect system of rules all at once. In a familiar part of the project, it is easier to notice where the assistant has found something useful and where it has confidently filled a gap in its knowledge.&lt;/p&gt;

&lt;p&gt;For a first attempt, I would choose a task where I can judge the answer myself. If the tool, the language, and the domain are all unfamiliar, it is much harder to distinguish help from a convincing mistake. Too much has to be taken on trust.&lt;/p&gt;

&lt;p&gt;I also need enough time to allow for an unsuccessful attempt. Restoring a service in a hurry is a poor moment to get acquainted with a new way of working. A small change I know how to make myself leaves me the option of stopping and finishing it the familiar way.&lt;/p&gt;

&lt;h2&gt;
  
  
  One question per attempt
&lt;/h2&gt;

&lt;p&gt;Back to the email. Suppose I need to understand where the recipient's name comes from before it reaches the template. I already have a rough idea of the route, so I can compare the explanation with what I know.&lt;/p&gt;

&lt;p&gt;The request can be very simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Find how the recipient's name reaches the email sent after an order is completed. Show the path through the files and functions. State separately what the code shows and what you are assuming. Do not change anything yet. I need to be able to follow this path myself using your answer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That final sentence sets a fairly specific expectation. I need an answer that lets me open a few places in the project and understand how they connect.&lt;/p&gt;

&lt;p&gt;The assistant might find the template quickly but miss the data transformation that happens before it. That is already a useful learning outcome: I can see where its search stopped and where I might have been too quick to agree. I can point out the omission and ask it to continue tracing the path. Or I might notice that the request was too broad and start with a particular handler instead.&lt;/p&gt;

&lt;p&gt;Another time, the answer may be accurate but so long that reading it takes more time than finding the function myself. I would narrow the next request: only the key steps, with links to the code, without an explanation of every line.&lt;/p&gt;

&lt;p&gt;That is how a personal understanding of the tool develops. It is made up of small observations: where to narrow a question, when to ask for evidence, which part of the context to supply at the outset. After a few attempts, these things begin to save attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do not change everything at the first inconvenience
&lt;/h2&gt;

&lt;p&gt;An unhelpful answer often makes you want to try another tool. Sometimes that really is sensible. But if you change the whole set of conditions every time, it becomes difficult to tell what you have actually learned.&lt;/p&gt;

&lt;p&gt;Suppose the assistant proposes far too large a change. On the first attempt, I asked it to “tidy up the notifications.” On the second, I specified a particular field, ruled out changes to the shared sending mechanism, and described the expected result. If the second answer was more useful, I learned something about defining a task. That lesson will stay with me even if I switch tools.&lt;/p&gt;

&lt;p&gt;So, for a few comparable tasks, I would keep my usual environment and change one technique. Today, I ask for the evidence behind a conclusion. Next time, I agree on a plan first. Later, I ask it to compare the completed change with the requirements. You can choose your own order; what matters is giving yourself time to notice what has changed in the work.&lt;/p&gt;

&lt;p&gt;These attempts need a clear limit. If half an hour has gone into persuading the assistant to notice an obvious file, it is perfectly reasonable to stop. I would note what did not work and return to the task the usual way. A learning experience does not become more valuable because it derails the entire day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leave yourself a few lines
&lt;/h2&gt;

&lt;p&gt;Once the email works, you want to close the task. That is precisely when it is easiest to lose the small discovery that made the whole exercise worthwhile.&lt;/p&gt;

&lt;p&gt;I would leave a few lines alongside the task: what I tried, where the answer helped, what needed correcting, and what I will repeat next time. For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The assistant found the template but did not trace the data preparation. Asking for the full path from the event to the email exposed the gap. Next time, I will start with that path. I am keeping responsibility for checking the result.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A week later, a note like that is more useful than a collection of polished prompts without context. It shows which problem the technique addressed and why it helped. If a similar situation comes up, I will have somewhere to start.&lt;/p&gt;

&lt;p&gt;When the same request comes up again and again, it can become a project rule or a small repeatable procedure. I arrived at this through my own repetitions: I did not want to explain the same constraints from scratch every time. But first, it helps to see how the rule works across several tasks. Otherwise, it is easy to spend an evening writing a lengthy instruction that nobody can confidently explain afterward.&lt;/p&gt;

&lt;p&gt;A rule needs a reason that makes sense without the chat history. “Show me a plan before making changes” helps us agree on the scope. “Do not commit without a separate instruction” keeps the decision to record the changes under my control. Agreements like these are easy to carry between tasks because their purpose is clear.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to notice an improvement
&lt;/h2&gt;

&lt;p&gt;The initial impression of speed can be misleading: the code appeared in a minute, but then it took an hour to work out what it did. I would therefore look at the whole journey to a result I am willing to accept.&lt;/p&gt;

&lt;p&gt;How much time went into explaining the task? Did it become easier to find the right place? How often did I have to bring the assistant back to the original constraints? Can I explain the change to a colleague afterward? These questions do not require a separate tracking system. Sometimes a short note beside the previous attempt is enough.&lt;/p&gt;

&lt;p&gt;Comparisons also deserve care. One email may be assembled in a single file; another may pass through several services. If the second task took longer, that does not by itself mean the new way of working became worse. It is more useful to look for recurring observations across similar tasks: fewer unnecessary changes, faster discovery of context, less need to explain the boundaries again.&lt;/p&gt;

&lt;p&gt;Sometimes the assistant does not make the work faster but does help me understand an unfamiliar part of the project. That counts as a result too, if that understanding was what I lacked. It is important to name it honestly, so I do not later promise myself time savings where I actually spent time learning.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can wait
&lt;/h2&gt;

&lt;p&gt;At almost any moment, there will be a new tool that looks interesting to try. But getting acquainted with it has to take time from somewhere. I would return to my setup when I can name a specific inconvenience in my work.&lt;/p&gt;

&lt;p&gt;If the assistant keeps forgetting project constraints, there is a reason to look into rules. If it spends too long finding context, I can examine what it can access and how the material is organized. If I want to continue a task from another device, I can explore a remote workspace. Each next step gains a purpose that makes its result easier to assess.&lt;/p&gt;

&lt;p&gt;Until then, some news can remain unread. Not every announcement needs to trigger a rearrangement of your working day.&lt;/p&gt;

&lt;p&gt;If I were starting now, I would choose one small, familiar task for the coming week, one technique, and a short note after the attempt. Then I would try the same technique again. After a few cycles, I would have material for the next decision: what to keep, what to change, and what I do not need yet.&lt;/p&gt;

&lt;p&gt;That spare half hour in the evening can still go toward an interesting walkthrough. Only now, it will be easier to recognize my own question in it. Why does the assistant lose the connection between the handler and the template, for example? Or how can I give it the constraints once instead of repeating them in every chat? Reading with a question like that is usually more useful than trying to learn everything just in case.&lt;/p&gt;

&lt;p&gt;And in the morning, there will be another ordinary task in the tracker. That is where I can pick up again.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>learning</category>
    </item>
    <item>
      <title>The Assistant Wrote the Code. How Do You Know It Is Ready to Ship?</title>
      <dc:creator>Mansur Fattakhov</dc:creator>
      <pubDate>Sat, 07 Nov 2026 03:34:25 +0000</pubDate>
      <link>https://dev.to/fattakhov/the-assistant-wrote-the-code-how-do-you-know-it-is-ready-to-ship-2fg0</link>
      <guid>https://dev.to/fattakhov/the-assistant-wrote-the-code-how-do-you-know-it-is-ready-to-ship-2fg0</guid>
      <description>&lt;p&gt;The email arrived. It has the right name, the right order number, and even the button leads where it should. The assistant says the task is done, the checks passed, and the changes are ready. At this point, closing the tab and moving on sounds very appealing. Especially if this little task has already taken up half the day.&lt;/p&gt;

&lt;p&gt;I would leave the tab open a little longer.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://mind.mansur.expert/en/giving-a-coding-assistant-a-task-in-an-existing-project/" rel="noopener noreferrer"&gt;previous article&lt;/a&gt;, we worked through an illustrative example: adding an email after an order is completed in an existing service. We first worked out what “completed” meant, found the existing notification mechanism, and agreed on the constraints. Now imagine the assistant has implemented that plan. We still have to decide whether we are ready to release the change to users.&lt;/p&gt;

&lt;p&gt;An email arriving at a test address is a satisfying moment. For the first time, you can see the result you were working toward. But alongside that email, I would reopen the original task. We promised the customer a notification at a particular point, and the team that email problems would not disrupt the order. I want to know how much of that promise the result actually confirms.&lt;/p&gt;

&lt;p&gt;A short request is enough to begin:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Show me the path from the relevant order event to the email being sent. What changed, and why? Compare the result with what we agreed: what has been checked, what evidence supports it, and what is still unknown. Do not fix anything yet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That last sentence helps. Otherwise, a discussion of the result can easily turn into another development cycle: the assistant spots an improvement and immediately starts making it. First, I want to look at what we already have.&lt;/p&gt;

&lt;p&gt;Suppose the reply points to the order handler, the code that adds a job to the queue, and the email template. I open the changes and follow that path myself. Where does the address come from? Why this particular status? What data goes into the message? Has the new code affected neighboring notifications?&lt;/p&gt;

&lt;p&gt;You do not need to learn the entire project for this review. But I do want to understand the parts responsible for the behavior we promised. If the explanation ends with “this uses the standard mechanism,” I would ask to see that mechanism. If the code is still unclear, I would bring in a colleague who knows it. A polished description does not make unfamiliar code familiar.&lt;/p&gt;

&lt;p&gt;Sometimes this is where an unexpected expansion of the task becomes visible. We were adding one email, yet the changes include a new way of processing every notification. There may have been a reason. It is worth recovering that reason before release, while the necessary change is still easy to separate from incidental cleanup. The more we changed, the more behavior we need to check.&lt;/p&gt;

&lt;p&gt;After reading the code, I would return to that email. How did it reach the inbox? Did we take a test order through the relevant state, or did we call the sending function directly? In the second case, we have learned something useful about the template and email delivery, but we have not yet traveled the path from order to notification. That needs a separate check.&lt;/p&gt;

&lt;p&gt;Imagine that works too. The order reaches the agreed state, and the email goes to the test address. Now we can introduce a little trouble into this happy story.&lt;/p&gt;

&lt;p&gt;For example, give the handler the same event again.&lt;/p&gt;

&lt;p&gt;In the last article, we agreed that a repeated event should not create an extra notification. Now we can make that agreement observable. How many jobs were created? How many times did the system try to send the email? What lets it recognize an event it has already processed? In a test, a replacement for the sending service can record calls so we can inspect them.&lt;/p&gt;

&lt;p&gt;Suppose the test list contains the reassuring name “does not send the email twice.” I would still look inside. It might only check that a particular field exists in the job. That is a useful check of the field. The promise about duplicate sending remains a separate question.&lt;/p&gt;

&lt;p&gt;It is easy to end up in a closed loop here: the assistant writes the code, writes tests to match that code, and reports that everything agrees. That is why I return to the behavior we discussed before implementation. What should the customer see? What should remain unchanged? What outcome would we consider wrong? These questions give the checks a reference point outside the implementation itself.&lt;/p&gt;

&lt;p&gt;The next problem is an unavailable email service. We reproduce it in a test environment or with a test double and see what happens to the order. Is its status preserved? Can the person using the service carry on? Can the team see that the notification was not sent, and is it clear what happens next?&lt;/p&gt;

&lt;p&gt;For our example, that matters more than checking once again that the email button looks nice. We agreed upfront that an email failure must not interfere with the order. Now we can verify that condition while the consequences stay inside a test.&lt;/p&gt;

&lt;p&gt;There is a more awkward case too: the email service accepted the message, but its acknowledgment never reached us. Sending may look unsuccessful to the application even though the email has gone out. If the assistant promises that duplicates are impossible under any circumstances, I would ask it to examine this particular moment. How does the system find out the result of the first attempt? What does it do if it cannot find out? We need to understand the solution’s actual limits and agree on acceptable behavior before promising the customer more than the system can deliver.&lt;/p&gt;

&lt;p&gt;These scenarios do not have to become a large, separate testing program. I would choose checks based on what this change could break. For an order email, that means timing, recipient, repeated events, and keeping order processing independent of email delivery. If the review shows that a shared notification mechanism has changed, we also need to look at its other callers.&lt;/p&gt;

&lt;p&gt;After that, “the checks passed” carries much more meaning. Ideally, the actual results are there too: which checks ran, in which environment, and what they confirmed. Separately, there should be a record of what could not be run. Perhaps the local tests passed, but access to the test email service was unavailable. That means part of the path still needs checking. Someone with access can do it, and we can return to the release decision once we have the result.&lt;/p&gt;

&lt;p&gt;I would also ask the assistant to compare the final changes with the original plan. The code may have changed while we investigated retries and failures. Checks performed before the last edit do not necessarily describe what we are about to ship. It helps to finish with one clearly identified version of the changes and check results for that version.&lt;/p&gt;

&lt;p&gt;Even then, one question remains before release, a question that rarely comes to mind while looking at an email in your own inbox: what will we do if a problem appears once users have the change?&lt;/p&gt;

&lt;p&gt;In our example, I would find out in advance whether we can stop the new emails while keeping order processing running. Who will notice a failure? Where can we see unsuccessful attempts? Will jobs already created continue sending after the feature is disabled? If that control is needed for a safe release and does not exist, we have another concrete task to complete beforehand.&lt;/p&gt;

&lt;p&gt;An ordinary code rollback cannot recall an email that has already been sent. Being able to stop has a practical purpose: it helps contain the consequences within the next few minutes, instead of continuing to send while the team works out what happened.&lt;/p&gt;

&lt;p&gt;By this point, the release decision usually feels calmer. I can explain what changed, show checks of the important scenarios, and name the remaining limits. I know how to observe the new sending behavior and how to stop it. If a significant “probably” remains in the story, it has an address: a specific piece of code, an unchecked scenario, or a question about product rules. We can work with that.&lt;/p&gt;

&lt;p&gt;I like how this approach gradually changes the conversation with the assistant too. Instead of a vague “check again that everything is definitely fine,” we ask questions with verifiable answers. We stop circling through reassurances and start examining specific behavior.&lt;/p&gt;

&lt;p&gt;The email in the test inbox still feels good. Only now I can see the whole path behind it: the right order, the right moment, the right recipient, and understood behavior when something fails. I can explain why I am ready to let this code meet users.&lt;/p&gt;

&lt;p&gt;Over time, these questions become a habit. That does not always require a separate practice project or a free month to learn a new tool. In the next article, we will look at learning to work with a coding assistant through everyday tasks, while the regular work is still there to be done.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>testing</category>
      <category>codereview</category>
    </item>
    <item>
      <title>How to Give a Coding Assistant a Task in an Existing Project</title>
      <dc:creator>Mansur Fattakhov</dc:creator>
      <pubDate>Tue, 20 Oct 2026 17:32:31 +0000</pubDate>
      <link>https://dev.to/fattakhov/how-to-give-a-coding-assistant-a-task-in-an-existing-project-7k4</link>
      <guid>https://dev.to/fattakhov/how-to-give-a-coding-assistant-a-task-in-an-existing-project-7k4</guid>
      <description>&lt;p&gt;“We just need to send an email after an order.” My favorite word in requests like this is “just.” While you're saying it, the work sounds almost finished: there's an order, there's the customer's address, and all that remains is connecting the two.&lt;/p&gt;

&lt;p&gt;Then you open the project.&lt;/p&gt;

&lt;p&gt;In one place the order is paid, in another it's packed, and in a third it's handed over for delivery. Emails are already being sent somewhere, but it's not yet clear whether you can reuse that mechanism. Before writing a few lines, you have to answer a question the original request never raised: what exactly do we mean by completing an order?&lt;/p&gt;

&lt;p&gt;Let's use this as a worked example. We have a running service and need to add an email to the customer when an order is completed. I want to use it to show how to approach a change with a coding assistant when a project already has plenty of established behavior and you can't hold all of it in your head.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://mind.mansur.expert/en/how-to-start-vibe-coding/" rel="noopener noreferrer"&gt;previous article&lt;/a&gt;, I suggested starting with a small change whose result you can see and check. This task is a little harder: before changing anything, we need to work out where its boundaries are.&lt;/p&gt;

&lt;p&gt;The most tempting first request is “find the order handler and add email sending.” It sounds specific. But several decisions are already hidden inside it: that a handler is the right place, that the email should be sent from there, and that we agree on the right moment.&lt;/p&gt;

&lt;p&gt;I'd start with what we know for certain: the customer should receive a notification when the order is completed, and email problems must not disrupt the order itself. Then I'd ask the assistant to first investigate the existing process.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We need to email the customer when an order is completed. A sending failure must not interfere with order processing. Don't change any code yet. Look at how an order moves through the system, where emails are already sent, and what checks exist nearby. Show the relevant places with references to the code. If “completed” is ambiguous, raise that as a question.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is an example request, not a required formula. What matters to me is having a goal, a constraint, and a next step at the start of the conversation. After that request, I expect an explanation I can compare with the project.&lt;/p&gt;

&lt;p&gt;To begin, the assistant needs access to the relevant repository and its working instructions: how the main parts are organized, how to run checks, and which conventions to follow. If I know where order processing starts, I'll provide that lead. If there's an issue describing the expected behavior, I'll share that too. Secrets and real customer data aren't needed for this: code and anonymized examples are enough.&lt;/p&gt;

&lt;p&gt;At the same time, I don't want to explain the entire project upfront. The investigation has a route: follow the order to the relevant state and find the existing notification mechanism. Other details can wait until that route leads to them.&lt;/p&gt;

&lt;p&gt;Suppose the assistant returns with some findings: the status changes in one module, emails go through a queue, and a similar notification already exists for another event. Now we have something we can verify. I can open those locations, read the surrounding code, and ask why this particular point was chosen.&lt;/p&gt;

&lt;p&gt;I'd pay special attention to “completed.” The project may have several similar statuses, each meaning something different. A field name helps you search, but it doesn't define the promise you make to the customer. A “Your order is complete” email after a successful payment could be quite a surprise if the order still needs to be packed.&lt;/p&gt;

&lt;p&gt;This is a useful point for the assistant to stop. It can show the options and explain what happens in each. Deciding which event to tell the customer about must follow the product's rules. If the answer is unknown, we need to establish it first. Otherwise, a few tidy lines will turn a guess into system behavior.&lt;/p&gt;

&lt;p&gt;Let's say we've settled when to send the email. Now we can examine what happens around that moment. Can the handler receive the same event twice? How does the project retry failed sends? Where can we see that an email never went out?&lt;/p&gt;

&lt;p&gt;It's easy to mistake these questions for overcomplicating a small task. But each brings us back to the same user request. The customer needs one clear notification. Running the handler again shouldn't turn into another email, and an unavailable mail service shouldn't break an order.&lt;/p&gt;

&lt;p&gt;Here I'd ask the assistant to distinguish what it found in the code from what it's assuming. There's a huge difference between “the neighboring handler already guards against duplicates” and “systems like this usually guard against duplicates.” We can inspect and reuse the first. The second still needs checking.&lt;/p&gt;

&lt;p&gt;Once those questions are resolved, the plan usually becomes much more concrete. We know which event matters, which sending mechanism already exists, how to add the email to it, and which scenarios to check. If something remains unknown, we can say so explicitly instead of hiding it inside the future implementation.&lt;/p&gt;

&lt;p&gt;Before editing, I'd ask for a short account of the plan: what changes, why here, and how we'll know we got the intended behavior. That conversation helps catch unnecessary scope. If one notification suddenly calls for rebuilding the entire email module, it's worth asking whether that's really unavoidable.&lt;/p&gt;

&lt;p&gt;Once we've agreed, we can move to code. The change needs its own branch with a clear starting state. If other tasks are running in parallel, a separate working tree—a worktree—can help. I use them to keep changes from different chats from getting mixed together. That separates the work, though the results still need to be checked for compatibility when merged.&lt;/p&gt;

&lt;p&gt;At this point the request can be brief: implement the agreed plan, use the existing sending mechanism, and show the changes and check results. By now, most of the meaning has been assembled in the conversation and grounded in the code.&lt;/p&gt;

&lt;p&gt;The checks should cover the behavior we agreed on. The email is produced after the right event. A repeated event is handled without creating an extra notification. A mail service failure doesn't interfere with the order, and the failure is visible through the project's established mechanism. Test addresses and a sending stub are suitable for these checks; real customers shouldn't be part of the experiment.&lt;/p&gt;

&lt;p&gt;I'd also ask which checks actually ran and what remains unverified. A persuasive explanation doesn't mean the implementation has been exercised through the relevant scenarios. If something couldn't be checked, that's useful information for the next step.&lt;/p&gt;

&lt;p&gt;This task leaves more than a new email behind. We've learned where the relevant order stage ends, how notifications are supposed to be sent, and which constraints matter. That knowledge belongs alongside the code or in the project documentation. Next time, we won't have to hunt for it again through a long conversation.&lt;/p&gt;

&lt;p&gt;I like that you can start with perfectly ordinary words. You don't need to know the solution's architecture immediately or compose an ideal prompt. Explain the outcome, name the important constraints, and assign a first step you can verify. The conversation then becomes more precise: specific code locations, behavioral questions, and a small plan emerge.&lt;/p&gt;

&lt;p&gt;“Add an email” gradually becomes a task both of you understand the same way. That shared understanding is what you can rely on when the assistant brings back the changes.&lt;/p&gt;

&lt;p&gt;The next question comes just before release: is reasonable-looking code and a set of passing checks enough? In the next article, we'll look at how to judge whether the result is ready to ship.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>agents</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How I Built a Workspace for My Coding Assistants</title>
      <dc:creator>Mansur Fattakhov</dc:creator>
      <pubDate>Sat, 10 Oct 2026 18:53:01 +0000</pubDate>
      <link>https://dev.to/fattakhov/how-i-built-a-workspace-for-my-coding-assistants-fjm</link>
      <guid>https://dev.to/fattakhov/how-i-built-a-workspace-for-my-coding-assistants-fjm</guid>
      <description>&lt;p&gt;I'm starting a series about how I gradually put together a workspace for my coding assistants. It will cover shared rules across projects, personal plans, working from my phone, and my attempts to stop depending on a single laptop. The story is still unfolding: I'm trying tools, changing my mind, and will be writing about how it goes. But first, I want to explain why I felt the need to build any of this.&lt;/p&gt;

&lt;p&gt;This story has a fairly modest beginning: I got tired of repeating the same requests.&lt;/p&gt;

&lt;p&gt;Don't commit until I've reviewed the changes. Don't add your own sign-off to the commit message. Small things, hardly worth interrupting your work for. Yet they came back in the next task, another project, a new conversation. And each time I had to briefly set aside whatever I'd opened my laptop to do and explain, once again, how I wanted us to work together.&lt;/p&gt;

&lt;p&gt;By then, &lt;a href="https://mind.mansur.expert/en/how-to-start-vibe-coding/" rel="noopener noreferrer"&gt;coding assistants were already part of my daily development work&lt;/a&gt;. With their help, I'd built quite a few products: things I used myself, things other people used, things real businesses relied on. Over time, I grew comfortable with that way of working. I learned where I was happy to trust an assistant and where I wanted to stop and inspect the result myself. Along with that experience came working agreements that, for some reason, I still had to carry from one conversation to the next.&lt;/p&gt;

&lt;p&gt;I generally enjoy learning new tools. Lately, though, barely had I settled into one before I could see another way to do part of the work. A few months later, I'd revisit my process and find more to reconsider. There was plenty to be curious about, but I had little appetite for explaining my own habits all over again with every change.&lt;/p&gt;

&lt;p&gt;I have a low tolerance for repetitive work. I can do it manually for a while, but somewhere in the back of my mind I'm already looking for a way out. So after making the same requests several times, I started writing them down as project rules.&lt;/p&gt;

&lt;p&gt;At first, it was a small collection of agreements in one repository. Each had a clear reason behind it: the assistant had rushed ahead here, I'd had to explain the workflow again there, and this was something I'd already asked for in the previous task. Rules appeared as they became necessary, and I remembered exactly why each one was there.&lt;/p&gt;

&lt;p&gt;Then I noticed that whole sequences of actions were repeating too. Creating a task, describing the intended change, preparing the result for review: I had preferences about those as well. That was how skills appeared alongside the rules, describing the way I usually worked. Things I'd once had to spell out every time gradually found a permanent place in the project.&lt;/p&gt;

&lt;p&gt;Naturally, I wanted to take the useful discoveries with me. If one project had become easier to work on, why not give the others the same treatment? I copied the agreements, refined them, and soon ran into the next nuisance: now I had to keep all the copies consistent. Change something in one place, then try to remember everywhere else it needed to go.&lt;/p&gt;

&lt;p&gt;When it came to sharing rules across projects and teams, rulesync proved useful. I collected the source rules in one main repository and set up a process that generated files for different agent tools and distributed them to the projects. The whole arrangement had a simple purpose: give me one place to make a change and a way for that change to reach the others.&lt;/p&gt;

&lt;p&gt;Little by little, the system acquired more detail. Habits, small conveniences, solutions to recurring frustrations all found their way into it. I liked being able to carry experience from one project into the next without spending so much time explaining it.&lt;/p&gt;

&lt;p&gt;Around the same time, I started planning a big camping trip for the following summer. I needed to put together a camp, work out what to bring, and think everything through. I was also paying more attention to my health and keeping track of what I ate. I kept notes for these things, and at first I didn't connect them with what I was gradually building around my development work.&lt;/p&gt;

&lt;p&gt;Then I noticed I was returning to them with some very familiar questions. What had I already decided? What still needed figuring out? Where had I left off? Before taking the next step, I had to reconstruct the previous ones. In my work projects, I was already learning to spare myself that effort by keeping context somewhere the assistant could read it too.&lt;/p&gt;

&lt;p&gt;I wanted to try the same approach with personal matters. Separate areas for development, health, camping, and other projects appeared in the shared repository. Each had its own notes and plans, while familiar ways of working could be used throughout.&lt;/p&gt;

&lt;p&gt;And so the camping plans moved in next to the code.&lt;/p&gt;

&lt;p&gt;It was surprisingly easy to get used to that arrangement. I could talk through plans with the assistant, return to recorded decisions, and work through what I still didn't understand. Starting a project no longer meant spending ages deciding where its first notes should go and how to avoid losing them. There was a place for it in an arrangement I already knew.&lt;/p&gt;

&lt;p&gt;I felt the change most clearly when picking things up again. When everything is scattered across separate notes, even a short break creates extra work: you have to remember what you meant, find the right entry, and gather your thoughts. Here, continuing became easier. The project retained what I'd already figured out about it, and I could move on.&lt;/p&gt;

&lt;p&gt;It made me noticeably more organized. More than that, I found myself wanting to return to my plans more often. They were easier to pick up, talk through, and see a next step in. The system I'd once started building out of irritation was slowly becoming a place I liked coming back to.&lt;/p&gt;

&lt;p&gt;That place, however, lived on my laptop.&lt;/p&gt;

&lt;p&gt;The files were there, Git was there, and that was where the agent ran, reading the material and making changes. As long as it was all about development, I barely noticed the tether: I sat down at a computer to work on code anyway. But thoughts about a trip or a personal plan rarely wait for you to open your laptop. They turn up in a taxi, in a coffee queue, in the middle of something else entirely.&lt;/p&gt;

&lt;p&gt;At those moments, I wanted to take out my phone and continue the conversation. Add something I'd just remembered. Clarify a question. Check where I'd left off. Everything I needed was already gathered together; I just needed to reach it.&lt;/p&gt;

&lt;p&gt;When tools for working with agents remotely became available, I was able to try that too. The laptop still did the work, but I could now direct it from my phone. My projects became easier to reach throughout the day: a quick check-in no longer required sitting down at the computer.&lt;/p&gt;

&lt;p&gt;You get used to that convenience quickly. Especially when the conversation follows you between devices and all the rules and material you've collected remain within reach. I'd spent so much time putting it together, and now I could finally use it where the need arose.&lt;/p&gt;

&lt;p&gt;All I had to do was remember to leave the laptop on.&lt;/p&gt;

&lt;p&gt;Sometimes I forgot. Or its battery ran out while I was traveling. Then the whole arrangement, with its agreements, projects, and remote access, had to wait until I got back to the computer.&lt;/p&gt;

&lt;p&gt;I think that was when I realized how far I'd come from those first requests about commits. I now had a workspace I wanted to reach at any moment. What remained was to make sure it didn't switch off along with my laptop.&lt;/p&gt;

&lt;p&gt;I'll come back to taking that life beyond the laptop. First, though, I want to look more closely at what we'd actually be moving. In the next article, I'll return to those early rules and skills: how they grew out of everyday requests, how I gathered them in one place, and how I used rulesync to distribute them across projects. Those small agreements were where everything else began.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>programming</category>
      <category>automation</category>
      <category>agents</category>
    </item>
    <item>
      <title>How to Start Vibe Coding When You Already Know How to Code</title>
      <dc:creator>Mansur Fattakhov</dc:creator>
      <pubDate>Thu, 08 Oct 2026 16:45:32 +0000</pubDate>
      <link>https://dev.to/fattakhov/how-to-start-vibe-coding-when-you-already-know-how-to-code-107l</link>
      <guid>https://dev.to/fattakhov/how-to-start-vibe-coding-when-you-already-know-how-to-code-107l</guid>
      <description>&lt;p&gt;When you already know how to code, your first attempt at working with a coding assistant can be frustrating. You could have opened the file and changed a few lines by now. Instead, you are explaining the task, clarifying details, and reading a long response. Code appears on the screen, along with a perfectly reasonable thought: “I could have done this myself already.”&lt;/p&gt;

&lt;p&gt;Perhaps you really would have finished that particular task faster. But one attempt still leaves you wondering how to fit an assistant into your usual work. Handing over an entire application feels risky; asking it to finish an obvious line feels pointless.&lt;/p&gt;

&lt;p&gt;I would start with a small change in a familiar project. Something where you can picture the result in advance and spot a mistake. That leaves you free to pay attention to the process itself: how to explain the task, what happens on the first attempt, and where your involvement is needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small annoyance on a large screen
&lt;/h2&gt;

&lt;p&gt;Imagine you have a personal blog. You usually open it on your laptop, and it looks fine. Then one day you open the page on a large monitor and notice that the article list has drifted apart. Titles sit far to the left, images stretch toward the right, and there is so much space between them that they no longer feel like parts of the same card.&lt;/p&gt;

&lt;p&gt;Meanwhile, the header and author description look normal. Nothing is particularly bothersome on your phone, either. You do not want to redesign the whole site. You want to bring the wide layout together while preserving what already works.&lt;/p&gt;

&lt;p&gt;This makes a useful first task. It has a visible defect and clear boundaries. You can decide what “done” means before you start: the list aligns with the rest of the content, images keep their proportions, titles remain readable, and the phone layout has no horizontal scrollbar.&lt;/p&gt;

&lt;p&gt;That clarity will come in handy when the assistant announces that everything is fixed. You will have something to compare its answer against.&lt;/p&gt;

&lt;p&gt;Before experimenting, save your current work in the usual way and create a separate branch. Being able to undo a change calmly makes it easier to try things without worrying about every unsuccessful attempt.&lt;/p&gt;

&lt;h2&gt;
  
  
  The conversation starts with what you see
&lt;/h2&gt;

&lt;p&gt;The assistant needs the same material a colleague would find useful: the page, a screenshot of the problem, and a few words about what bothers you. If the tool can read your project, you can ask it to find the template and styles. In a regular chat, you will need to provide the relevant snippets yourself. Page code and a screenshot are enough to investigate the layout; credentials and user data have no role here.&lt;/p&gt;

&lt;p&gt;An ordinary description is enough for the first request:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;On my blog's author page, the article list spreads wider than the header and author description on a large screen. The titles and images end up too far apart. I am happy with the layout on smaller screens.&lt;/p&gt;

&lt;p&gt;Find what controls the list's width, explain the cause, and suggest a small change. Do not edit any files yet. I have attached a screenshot and the page address.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You do not have to guess the cause or dictate which class to add in your first message. Describing what you observe and asking the assistant to investigate is enough. It also gives you a chance to check whether you understand the task the same way before changes appear in the project.&lt;/p&gt;

&lt;p&gt;A useful explanation can be checked against the code. For example, the assistant might show that the header sits inside a container with a width limit, while the article list sits outside it. You open the template and see whether that is true. If the explanation does not match the file, it is better to discuss the discrepancy immediately.&lt;/p&gt;

&lt;p&gt;At this point, it is worth asking which other pages use the same template or style. A small change can have neighbours: fixing the author page might also alter a category page without you noticing.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the code arrives
&lt;/h2&gt;

&lt;p&gt;Once the explanation makes sense, ask the assistant to apply the proposed change and show the diff. The work now becomes familiar: you have a set of changes to read.&lt;/p&gt;

&lt;p&gt;It is tempting to glance at the browser and move on. But a few minutes with the changed lines help you understand what happened. Why was the width constraint added here? Was an existing container reused? Where did that change to the neighbouring block come from?&lt;/p&gt;

&lt;p&gt;If the assistant also renamed classes, reformatted the whole file, and “improved” a couple of other things, ask why those changes are necessary. A compact diff is easier to connect to the original task. Anything unrelated can wait for a separate conversation.&lt;/p&gt;

&lt;p&gt;There is no need to pretend you understand an unfamiliar construct. You can work through it, ask why it is needed, or suggest an approach already used in the project. After all, the code will still be in your repository after you close the chat.&lt;/p&gt;

&lt;p&gt;A regular chat adds one manual step: copying the suggested snippet into the file. After that, the process is the same: inspect the diff and open the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  The page needs to survive resizing
&lt;/h2&gt;

&lt;p&gt;The blog may now look excellent on a large monitor. That is satisfying, but the task had another condition: preserving smaller screens.&lt;/p&gt;

&lt;p&gt;I would open the page in a wide window, at a typical laptop width, and at a narrow phone width. Then I would simply drag the edge of the window. Sometimes the problem appears between your chosen sizes: a title suddenly wraps, an image becomes too narrow, or an unexpected gap appears.&lt;/p&gt;

&lt;p&gt;If the change affects a shared template, check another page that uses it. The build and project checks are useful too, but they will not tell you whether a title is comfortable to read next to its image. It helps to ask what the assistant actually checked and what still needs your attention.&lt;/p&gt;

&lt;p&gt;Suppose the wide layout looks good, but an extra gap has appeared on the left on mobile. That is a concrete observation you can bring back to the conversation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The article list now aligns correctly on a wide screen. At a width of 390 pixels, there is an extra gap on the left. Here is a screenshot. Find which part of the last change introduced it and suggest a fix that preserves the wide-screen result.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A working rhythm gradually emerges. You point out a discrepancy, the assistant investigates, and you check the next version. If changes keep accumulating and the explanation stops matching the result, you can stop and return to the saved state. Sometimes restating a narrow task is easier than continuing a tangled chain of fixes.&lt;/p&gt;

&lt;h2&gt;
  
  
  After the first attempt
&lt;/h2&gt;

&lt;p&gt;Once the page looks right, pause briefly before moving to the next task. Where did the assistant save time? What was difficult to explain? Which mistake did you catch yourself, and how did you resolve it?&lt;/p&gt;

&lt;p&gt;Those answers are more useful than an overall feeling of liking or disliking the experience. Perhaps finding unfamiliar templates was quick, but discussing the design took too long. Or perhaps the code worked immediately, but you had to describe your acceptance criteria much more precisely than usual.&lt;/p&gt;

&lt;p&gt;A small blog fix is enough for a first attempt. You can approach input validation or a report change in the same way: choose a familiar task, picture the right outcome, and work through to checking it.&lt;/p&gt;

&lt;p&gt;One such change is enough to get started. Afterwards, you will have your own experience: useful wording, unnecessary edits, and places where work stalled without your understanding of the project. That gives you something to build on when choosing the next task.&lt;/p&gt;

&lt;p&gt;I wrote about my attitude toward this way of working in &lt;a href="https://mind.mansur.expert/en/yes-i-vibecode/" rel="noopener noreferrer"&gt;“Yes, I Vibecode”&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>vibecoding</category>
      <category>ai</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Alerts Worth Waking Up For</title>
      <dc:creator>Mansur Fattakhov</dc:creator>
      <pubDate>Tue, 06 Oct 2026 04:32:28 +0000</pubDate>
      <link>https://dev.to/fattakhov/alerts-worth-waking-up-for-5aof</link>
      <guid>https://dev.to/fattakhov/alerts-worth-waking-up-for-5aof</guid>
      <description>&lt;p&gt;The most honest way to test your monitoring is to wait until everything goes down and then see who tells you about it first: the system or a real person. I once had a project where that examination arrived without warning and ended badly. The machines sat with a single provider, Kubernetes with Prometheus and Grafana lived right there among them, and nobody checked anything from the outside — why bother, it seemed, when everything is plainly visible from within. On the day the provider took every machine down at once, not a single message arrived. The graphs did not turn red, because they had stopped updating; the rules did not fire, because there was nothing left to fire; the notification channel held its ordinary workday silence, the very silence that for all the preceding months had meant that things were going well.&lt;/p&gt;

&lt;p&gt;We found out about the outage not from monitoring and not straight away. And this was not a story about poorly chosen thresholds or unfortunate rules — it is a story about how monitoring that runs on the same servers as the services falls silent together with them, and monitoring that has fallen silent looks exactly like monitoring for which everything is fine.&lt;/p&gt;

&lt;p&gt;Ever since, I have designed alerts not from the question "what else could we measure" but from the question "and if this does not work, who will notice, and when". What follows is what comes of that: which messages are worth sending to a human being, where thresholds come from, and why a good share of the alerts in an average project ought not to exist at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  An alert is a promise that someone will get up
&lt;/h2&gt;

&lt;p&gt;It all starts with a single distinction that spares more nerves than any technique. An alert is neither an observation nor a curiosity, but a promise: when it arrives, a particular person will take a particular action. If no action is implied, what you have in front of you is not an alert but a graph, and its place is on a dashboard people visit of their own free will, not in a channel that wakes them.&lt;/p&gt;

&lt;p&gt;The easiest way to tell the two apart is a thought experiment: imagine the message arriving at three in the morning. If the only thing you will do is glance at it and lie back down, then it has no business arriving at night; and if you would do nothing about it in the daytime either, then it has no business arriving at all. I have written about this before in &lt;a href="https://mind.mansur.expert/en/kpis-that-dont-turn-into-a-stick/" rel="noopener noreferrer"&gt;the piece on KPIs&lt;/a&gt;, where the share of alerts that people actually respond to turned out to be a far more honest metric than their number: an alert everyone ignores does more harm than a missing one, because it does not merely keep quiet about a problem — it trains people not to look at the rest of them either.&lt;/p&gt;

&lt;p&gt;This rule has a reverse side that gets remembered less often. If action is implied and there is no alert, then you are seriously counting on someone noticing the breakage by accident. Usually it is the users who notice, and usually later than you would have liked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Silence that looks like order
&lt;/h2&gt;

&lt;p&gt;The breakage hardest to see does not look like a red graph but like the absence of a graph. A process died, an agent dropped off, the network cut away half the environment — and the panel does not flush crimson, it simply freezes. The last green dot is indistinguishable from a normal one, and an eye trained to hunt for red slides calmly past.&lt;/p&gt;

&lt;p&gt;The rule "the service is not answering the scraper" is no salvation here, because a dead host fails to answer not out of pain but because, as far as the scraper is concerned, it no longer exists. What is needed is a separate class of rules — ones that watch not for bad values but for the very fact that data is arriving, and that fire when a metric stays quiet longer than is reasonable. I keep such a rule for every host, and it is the only thing that catches "everything has vanished" rather than "everything has broken".&lt;/p&gt;

&lt;p&gt;Out of the same story with the provider grows a second conclusion: part of your checks must live on the outside. Internal monitoring knows everything about the services but dies along with them; an external check knows shamefully little — whether the domain answers, whether the certificate is valid — yet it outlives the collapse of everything else and asks precisely what a user would ask. Two loops cost pennies and cover different classes of outage: from the inside you see that things have gone bad, from the outside that you are no longer there.&lt;/p&gt;

&lt;p&gt;And the last layer of the same onion is a watchman over the watchman. Rules may fire faithfully while the messages never arrive: the bot token went stale, the notification service went down, someone renamed the channel. The silence that results is indistinguishable from the silence of a calm day, and here the circle closes exactly where it began. So I keep alerts for notifications failing to be delivered and for the external monitor being unreachable — and yes, I understand that this watchman could in theory fall silent too; it is simply that with every layer the probability of a simultaneous failure grows less and less interesting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outages known about in advance
&lt;/h2&gt;

&lt;p&gt;There is a separate breed of breakage that it is plainly shameful to learn about after the fact, because the date is known in advance and written into the object itself: a certificate expires, a domain runs out, an integration token goes stale. All of it happens on schedule and, at the appointed hour, brings the service down without fail.&lt;/p&gt;

&lt;p&gt;On a busy website such an outage does not live long: the certificate expired, the page stopped opening, a quarter of an hour later somebody wrote in the chat, another quarter and it was mended. But on a website that gets only a few visits a day there is nobody to be first — and that same expired certificate quietly holds the page shut for hours, while you learn of it when you finally drop by yourself. The most galling part is that it is prevented by a single line: a warning two weeks out, then a week, then a day. I keep such checks in the external loop, the very one that will outlive the fall of the internal one.&lt;/p&gt;

&lt;p&gt;Beside them stand the background jobs, and the logic is the same, only turned inside out. A backup, a nightly export, a cleanup of old data do not break loudly: they do not fall over with an error, they simply stop happening. So what has to be watched is not the failure but the absence of a successful run: my blog's nightly backup knocks on the monitor itself after every successful attempt, and if no knock has come by morning, a message arrives — regardless of whether the process died, never started on schedule, or quietly hung halfway through.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where thresholds come from
&lt;/h2&gt;

&lt;p&gt;A threshold taken from thin air is the chief producer of pointless nighttime wake-ups. "Let us say eighty percent" sounds judicious right up to the day it turns out that the service lives perfectly well at eighty-five and wakes you every night until somebody thinks to look at what those numbers have been over the past six months. The order ought to be the reverse: first the actual distribution, then a threshold slightly above the usual maximum, and after the very first false firing — higher still, because reality always proves noisier than it looks on a week's worth of graph.&lt;/p&gt;

&lt;p&gt;The second thing without which a threshold is useless is the "for" duration. The rule must require the condition to hold for some time, otherwise any one-second spike will raise the alarm and you will find yourself in front of a laptop at the moment when everything has long since been fine. A couple of nights like that are enough for a person to start opening the messages every other time, and from there see above: an alert that gets ignored is worse than no alert at all.&lt;/p&gt;

&lt;p&gt;There is a third consideration as well, far less obvious: a threshold lives not in a vacuum but in concert with the rest of the automation, and that automation has to be known. On my server there is a cleanup after the build runner which begins pruning images when about three quarters of the disk is taken. The free-space alert is set not where the number looks prettier but where it falls between the soft and the hard level of that cleanup: let it fire earlier and it will shout every time the system is already tidying after itself; let it fire later and it arrives when it is too late to mend anything and images must be torn out by hand on a live server. A good threshold is always an answer to the question "what is already happening without me", and not a round number out of one's head.&lt;/p&gt;

&lt;h2&gt;
  
  
  Severity as a promise, not as ornament
&lt;/h2&gt;

&lt;p&gt;Severity levels make sense only if behind each of them stands an obligation that can be checked. I have three, and they read literally: critical wakes me at night, warning I look at today, info does not reach the main channel at all. The moment a fourth level appears, of the "important, but not that important" sort, the system begins to come apart, because the boundary between neighbouring levels ceases to be checkable and therefore ceases to be observed.&lt;/p&gt;

&lt;p&gt;Hence the most reliable symptom of ailing monitoring: if everything of yours is critical, then nothing is critical. Marking by severity says nothing about how dear a metric is to you; it says what a person is obliged to do on seeing the message — and if the answer is the same for every level, the levels can safely be thrown out.&lt;/p&gt;

&lt;h2&gt;
  
  
  One cause, one message
&lt;/h2&gt;

&lt;p&gt;When a node goes down, a conscientiously configured system sends two dozen messages: about the node itself and about every container that lived on it. Formally it is all true; in essence it is noise, and the single line that matters drowns in it. So messages have to be grouped by cause, and repeats sent no more often than once every few hours while the problem is alive: a person needs one signal and one place to look, not a chronicle of disintegration.&lt;/p&gt;

&lt;p&gt;And without fail — a separate message about recovery. Without it there is no telling whether the outage has ended or you have merely turned away from the channel; and the habit of assuming that things will somehow sort themselves out will one day coincide with the case where they did not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alerts on meaning, not only on mechanics
&lt;/h2&gt;

&lt;p&gt;Technical rules answer honestly the question of whether the system is working, but say nothing about whether it is doing the thing it exists for. A service can return an impeccable HTTP 200 to every request, all the graphs will be green and even, and inside it is already empty: the data supplier broke, the integration dropped off, an update changed the format, and the page now honestly and quickly shows nothing at all.&lt;/p&gt;

&lt;p&gt;So on top of the technical ones I keep a few rules that watch for business-level signals: click-throughs have fallen to zero, the data sources have stopped answering, a regular job has not refreshed the data mart by its appointed hour. Such alerts catch precisely what the mechanics miss, and it is usually they that save your reputation, because a user does not see your uptime — they see an empty page and draw a conclusion about you as a whole.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rules live in the repository
&lt;/h2&gt;

&lt;p&gt;Last in order but not in importance: alerts are code, and they are worth handling as code. The rules lie next to the configuration, pass a check before rollout, and change by the ordinary working process rather than by mouse in an interface on the fly. The reason is not fastidiousness: monitoring is almost always patched in a hurry and in a foul mood, right after an outage, and it is exactly those changes that matter most to keep in the history — so that six months later one can see not only what the threshold is, but what kind of night gave birth to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checklist
&lt;/h2&gt;

&lt;p&gt;⬜ For every alert it is clear who does what when it arrives&lt;br&gt;&lt;br&gt;
⬜ There are rules for the absence of data, not only for bad data&lt;br&gt;&lt;br&gt;
⬜ There is an external loop that will outlive the fall of the internal one&lt;br&gt;&lt;br&gt;
⬜ There is an alert for the failure of notification delivery itself&lt;br&gt;&lt;br&gt;
⬜ Certificate, domain and token expiry dates are checked in advance&lt;br&gt;&lt;br&gt;
⬜ Background jobs alert on the absence of a successful run, not only on failure&lt;br&gt;&lt;br&gt;
⬜ Thresholds are taken from actual values and have a "for" duration&lt;br&gt;&lt;br&gt;
⬜ Severity levels mean a concrete promise, and critical is not on everything&lt;br&gt;&lt;br&gt;
⬜ One cause yields one message, and recovery arrives separately&lt;br&gt;&lt;br&gt;
⬜ There is at least one alert on business meaning, not only on mechanics&lt;br&gt;&lt;br&gt;
⬜ The rules lie in the repository and pass a check before rollout&lt;/p&gt;

</description>
      <category>devops</category>
      <category>monitoring</category>
      <category>observability</category>
      <category>sre</category>
    </item>
    <item>
      <title>Yes, I Vibecode</title>
      <dc:creator>Mansur Fattakhov</dc:creator>
      <pubDate>Sun, 27 Sep 2026 15:40:42 +0000</pubDate>
      <link>https://dev.to/fattakhov/yes-i-vibecode-dmc</link>
      <guid>https://dev.to/fattakhov/yes-i-vibecode-dmc</guid>
      <description>&lt;p&gt;Only a year ago, putting together a working application from a single description in plain language was a party trick people bragged about on Twitter. Today that is how internal tools get built in teams that used to wait months for a slot in a developer's queue, and nobody finds it remarkable any more. Vibecoding has stopped being a sideshow and become something anyone can simply do — much the way building a website without knowing HTML once stopped being a miracle.&lt;/p&gt;

&lt;p&gt;Along with that, a stock reply has appeared in developers' conversations, and I hear it constantly now: "vibecoding is not coding." It is delivered in every possible tone, from the condescending to the openly angry, but the meaning is always the same: what those people are doing does not count. I thought that way myself for quite a while, and that is exactly why I want to work out why it is a bad reaction — not because it is factually wrong, but because it misses the target and spoils the person who says it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the irritation comes from
&lt;/h2&gt;

&lt;p&gt;It is worth starting with the fact that the irritation is entirely understandable. Behind the person feeling it lie years spent on things nobody sees from the outside: algorithms that had to be learned twice, nights spent debugging someone else's code without a single line of documentation, postmortems in which it turned out that you were the cause yourself, and the slowly collected bruises out of which professional instinct is eventually made. And right next to that, someone describes a task in the words they would use with their grandmother, and gets the result in one evening.&lt;/p&gt;

&lt;p&gt;It feels as though the part that was hard is precisely the part being devalued. As though years of experience had collapsed into an ability to phrase things neatly in plain language. This is not greed and not snobbery — it is an ordinary human jealousy over one's own labour, and it would be strange not to feel it. The problem is not the feeling but the conclusion drawn from it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honestly: I thought the same
&lt;/h2&gt;

&lt;p&gt;At first I caught myself on exactly that thought — nonsense, not serious, toys for people who do not want to learn properly. I watched a couple of demos, looked inside the resulting code, saw things there I would not let into prod even at the point of a deadline, and calmed down: not a competitor.&lt;/p&gt;

&lt;p&gt;The mistake was not in my assessment of the code — the code really was mediocre. The mistake was that I was comparing the wrong things with each other: someone else's result after one evening against my own result after months of work. The comparison that would have made sense sounds different — what I myself can do with this tool, and how that will differ from what I do without it. The moment I put the question in that form, there was no one left to argue with.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tool does not level, it amplifies
&lt;/h2&gt;

&lt;p&gt;It turned out that the better a person understands how systems are built, the more they get out of code generation. That contradicts the first impression — you would think a tool that writes code for you ought to level out the strong and the weak. It does not level anything out; it multiplies what is already there.&lt;/p&gt;

&lt;p&gt;The reason is simple: an engineer's work never did come down to typing text. It begins long before the first line — with framing the problem, where you have to formulate not only what the result should be, but also what happens when everything goes wrong: what we do on a timeout, what we do when two requests race each other, where the load limit lies beyond which the solution stops working. Half the problems of the future task are visible right here, and they are visible to the person who has already stepped on them.&lt;/p&gt;

&lt;p&gt;Next you need context the model cannot know in principle: how this particular service is put together, why there is a queue here rather than a direct call, which decisions were already taken and written down a year ago and why they should not be reopened along the way. Then the resulting code has to be read with the eyes of the person who will answer for it in prod — which means looking not for typos but for the quiet places: an unhandled timeout, idempotency lost somewhere along the road, a query that works beautifully on a hundred test rows and falls over on a real million. And finally all of it has to be rolled out, the metrics watched, rolled back if it went wrong, and answered for — that part is delegated to absolutely no one and is covered by no tool at all.&lt;/p&gt;

&lt;p&gt;Generation covers the middle: the actual writing of the code, the very part that was already the fastest for an experienced person. The edges stay where they were, and it is in them that everything an engineer is paid for lives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two different occupations under one name
&lt;/h2&gt;

&lt;p&gt;This, it seems to me, is where the whole argument grows from: one word is used for two different things, and then people are surprised that they cannot come to an agreement.&lt;/p&gt;

&lt;p&gt;The first is vibecoding as entertainment and as a way into the profession. Someone puts together something of their own, takes pleasure in the fact that what they had in mind works, and sees a result they would never in their life have seen without this tool. For most of them it will stay an evening pastime, and there is absolutely nothing wrong with that. More than that, it is through such trifles that new people come into the industry — and they come several years earlier than they would have come through a textbook on algorithms, if they ever arrived at all.&lt;/p&gt;

&lt;p&gt;The second is an engineer who uses generation professionally. The tool is exactly the same, but the tasks are of a different scale and carry a different cost of error: data migrations that cannot be replayed, integrations with someone else's payment systems, rewriting a service that is holding load right now, infrastructure you answer for with night shifts. This is no longer vibecoding in the sense in which the word is usually used — it is engineering work in which generation stands in the same row as the debugger and the profiler, and just as surely decides nothing on its own.&lt;/p&gt;

&lt;p&gt;The difference, in short, is not in the tool but in what happens before it and after it.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the musician at his friends' gig
&lt;/h2&gt;

&lt;p&gt;When an engineer, in reply to "but you're vibecoding," starts explaining that he is not vibecoding at all but doing real, difficult work, he is arguing about a word — and from the outside that looks not like an argument but like a defence of status, whatever he may actually have meant.&lt;/p&gt;

&lt;p&gt;I like the comparison with music here. Picture a musician from a good orchestra who has come to a concert by his friends' band: they got together six months ago, they play in a garage, they miss half the notes, the drummer rushes. And there he is all evening, wincing and telling everyone around him how bad this is and how much he dislikes it. Formally he is absolutely right — they really do play worse than he does. In human terms he has simply ruined the evening for people who took nothing away from him.&lt;/p&gt;

&lt;p&gt;The people putting together their first application from a description today are the friends in the garage. They are not laying claim to a seat in the orchestra and they are not taking it away from anyone. Some of them will come a year from now to learn to play seriously, and it will be precisely because they had a good evening, not because someone explained to them how bad they were.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to answer it
&lt;/h2&gt;

&lt;p&gt;So when you are told that you are vibecoding, the best thing you can do is not to take offence. Smile and agree: yes, and I do it brilliantly and professionally. After that the conversation either moves into a normal professional channel or ends — and both options are better than an argument about terms.&lt;/p&gt;

&lt;p&gt;Along the way I have forbidden myself three things. Not to prove that my work is difficult: if its difficulty is not visible in the result, no words will explain it, and if it is visible, there is nothing left to explain. Not to correct other people's terminology: the phrase "this isn't vibecoding, it's engineering with code generation" has never yet made anyone smarter, but it reliably ends the conversation. And most importantly — not to devalue someone else's interest: a person showing you a thing they put together over the weekend is not waiting for a review, what they need is for someone to notice that they made it at all, and that is worth saying out loud even if inside there is terrifying code and three hardcoded passwords.&lt;/p&gt;

&lt;p&gt;The last of these matters more to me than the first two. Interest is the rarest and most fragile resource in our profession: it is put out with a single condescending comment, and lighting it again afterwards almost never works.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for an engineer
&lt;/h2&gt;

&lt;p&gt;The skill does not disappear, it shifts. Value moves out of the speed of typing code — where, frankly, there was never much of it to begin with — and over to where there was always the most of it: understanding the domain, framing a task so that it can be solved at all, seeing the boundaries, and answering for what runs in prod at three in the morning.&lt;/p&gt;

&lt;p&gt;That, incidentally, is exactly the same set the team lead is responsible for — &lt;a href="https://mind.mansur.expert/en/kpis-that-dont-turn-into-a-stick/" rel="noopener noreferrer"&gt;there was a separate post&lt;/a&gt; about it. And it is exactly what cannot be generated: no model knows what downtime counts as acceptable at your company, why the last migration was rolled back at the final moment, and whom you need to talk to before touching this service.&lt;/p&gt;

&lt;p&gt;And how a working everyday tool gets assembled out of generation — projects, goals, routine — I will tell separately, closer to December.&lt;/p&gt;

&lt;h2&gt;
  
  
  In short
&lt;/h2&gt;

&lt;p&gt;⬜ Vibecoding has gone mainstream; arguing with that is too late and pointless&lt;br&gt;&lt;br&gt;
⬜ The tool does not level, it amplifies: the better you understand systems, the greater the return&lt;br&gt;&lt;br&gt;
⬜ Generation covers the middle of the work, but not the framing, the context, the review or the responsibility&lt;br&gt;&lt;br&gt;
⬜ Entertainment and engineering work share one word — they are different occupations, and both are fine&lt;br&gt;&lt;br&gt;
⬜ If you are told "you're vibecoding," agree and add that you do it professionally&lt;br&gt;&lt;br&gt;
⬜ Do not review someone else's interest: notice it and praise them for having made the thing&lt;/p&gt;

</description>
      <category>ai</category>
      <category>vibecoding</category>
      <category>programming</category>
      <category>career</category>
    </item>
    <item>
      <title>KPIs That Don't Turn Into a Stick</title>
      <dc:creator>Mansur Fattakhov</dc:creator>
      <pubDate>Sun, 27 Sep 2026 14:42:03 +0000</pubDate>
      <link>https://dev.to/fattakhov/kpis-that-dont-turn-into-a-stick-44oj</link>
      <guid>https://dev.to/fattakhov/kpis-that-dont-turn-into-a-stick-44oj</guid>
      <description>&lt;p&gt;You now have several teams, each with a lead, and you want the system to run predictably — without diving into every team every day. The first reflex is to "hand out KPIs." And that is exactly the place where it's easy to ruin everything.&lt;/p&gt;

&lt;p&gt;It all breaks against one regularity the economist Charles Goodhart formulated half a century ago: &lt;strong&gt;once a metric becomes a target, it stops being a good metric.&lt;/strong&gt; Not because people are dishonest, but because you can almost always work on the number directly — bypassing the thing it was introduced for. Give every lead a share of uptime and you'll get cautious releases and arguments about whose incident it was, not a more reliable product.&lt;/p&gt;

&lt;p&gt;What follows is how I built KPIs for team leads on a gamedev project with six teams while working around that trap. The main question there turned out to be not "which metrics do we take" but "what is a lead responsible for in the first place."&lt;/p&gt;

&lt;p&gt;This is a spin-off of the &lt;a href="https://mind.mansur.expert/en/how-to-bring-order-to-a-project-track-map/" rel="noopener noreferrer"&gt;"How to bring order to a project"&lt;/a&gt; series — this time about management.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it started
&lt;/h2&gt;

&lt;p&gt;Six teams: the mobile client, the launcher, the game server and the rest. At the C-level we wanted transparency — to see how things were going without climbing into every team daily.&lt;/p&gt;

&lt;p&gt;But there was also a concrete trigger, not just manageability in general. In several teams, churn was eating the focus on the things that never look urgent: ANRs and crashes. Nobody worked on them deliberately — not because they couldn't, but because it was nobody's zone. A feature has a deadline and an owner; a stability number has neither. So beyond transparency, the KPIs had to place responsibility: not "who's to blame for the crash," but "whose zone of attention is this at all."&lt;/p&gt;

&lt;p&gt;The starting point was the CTO's own KPI. It already existed — put together quickly, but it existed, and that turned out to matter more than the precision of the wording: there was a single outcome everyone worked toward. The mistake would have been to slice it into shares and hand them down — more on that trap below. What was valuable was something else: it set a direction, and from there you had to answer your own question — what is this particular lead responsible for within that direction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the C-level wants this — honestly
&lt;/h2&gt;

&lt;p&gt;The motivation from the top usually sounds like "transparency and manageability," but it's worth unpacking all the way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Seeing the state of the teams without micromanagement.&lt;/strong&gt; Not "what is everyone busy with" but "is the system healthy" — and noticing degradation before an incident.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Predictability.&lt;/strong&gt; Promises to the business rest on something measurable, not on a feeling that "it seems fine."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scaling yourself.&lt;/strong&gt; A CTO with six teams physically cannot be the entry point to every decision — lead KPIs are a way to delegate responsibility, not tasks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And right here is the honest trap you have to see in advance: &lt;strong&gt;the easiest way to "decompose the CTO's KPI" is to cut it into pieces and hand them down.&lt;/strong&gt; The CTO's goal is "uptime and product quality" — so give each lead a share of uptime. It looks logical and works badly: the lead gets a metric they only partly influence, starts optimizing the number instead of the system, and defends themselves with reports instead of doing the work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the leads think about it
&lt;/h2&gt;

&lt;p&gt;Leads have their own set of motivations — and fears, and ignoring them is the most expensive option of all:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What a lead gets from honest KPIs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clarity: "what is expected of me" stops being telepathy — these are literally the &lt;a href="https://mind.mansur.expert/en/people-bus-factor-expectations-and-day-one/" rel="noopener noreferrer"&gt;expectations spoken out loud&lt;/a&gt; from the people track, only at the level of roles;&lt;/li&gt;
&lt;li&gt;protection: written criteria are insurance against arbitrary evaluation "by mood" and against rules changed after the fact;&lt;/li&gt;
&lt;li&gt;arguments: "we need one more person" sounds weak; "we won't hold the stated recovery time without a second on-call" sounds strong.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What a lead is afraid of:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;that the metrics will become a stick: any red number is grounds for an inquest;&lt;/li&gt;
&lt;li&gt;that they'll have to work "for the number": producing reporting instead of value;&lt;/li&gt;
&lt;li&gt;that the goals will be set above their head — from a world where their teams don't exist.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything below is, in essence, an answer to those fears.&lt;/p&gt;

&lt;h2&gt;
  
  
  The key turn: a lead owns the system, not the outcome
&lt;/h2&gt;

&lt;p&gt;The central decision everything rests on: &lt;strong&gt;a lead is responsible not for the final business metric, but for the system and the processes that make the outcome inevitable.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CTO KPI → processes → team → outcome
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The outcome is a consequence of a working system, not the subject of a monthly evaluation. The QA lead doesn't own "zero bugs in production" — they own the regression suite being current, a verdict existing before a release, and defects that escaped to production being reviewed so they close the hole in the system. The infra lead doesn't own "zero incidents" — they own alerts being meaningful, runbooks existing, and MTTR going down.&lt;/p&gt;

&lt;p&gt;And this is the answer to the question "how do I make everyone work on product quality rather than on the CTO's KPI": &lt;strong&gt;don't translate the outcome downward — ask about the health of the system that produces that outcome.&lt;/strong&gt; You can "work on" an outcome number in a report; you can't do that to a working system — it either exists or it doesn't.&lt;/p&gt;

&lt;p&gt;The evaluation is then phrased through the system as well, in three levels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Good&lt;/strong&gt; — the system works and is developing; the outcome is a stable consequence of it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Satisfactory&lt;/strong&gt; — the system exists but has gaps: coverage is incomplete, it runs irregularly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unsatisfactory&lt;/strong&gt; — there is no system; the work is reactive, held together by manual labor and heroics.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice: "unsatisfactory" is not "bad numbers," it's "no system." The difference is fundamental — and it takes the fear of the stick away.&lt;/p&gt;

&lt;h2&gt;
  
  
  The construction: standing KPIs plus KPIs of the month
&lt;/h2&gt;

&lt;p&gt;The second finding that made life much easier was to split KPIs into two kinds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standing (S)&lt;/strong&gt; — the hygiene of the role: the same bar every month. Monitoring covers the services, the regression suite is current, RCAs get written for incidents, releases go through a verdict. They don't change, they aren't renegotiated — they simply have to be met.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KPIs of the month (M)&lt;/strong&gt; — three or four goals set for the month: close a specific piece of tech debt, raise feature coverage, lower MTTR, run a Game Day. They live for one cycle and are set anew at the monthly review.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The standing ones give stability ("what normal means"), the monthly ones give movement ("what we're improving right now"). Without S, monthly goals turn into firefighting; without M, standing ones turn into stagnation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Metrics: ground them in facts, not in wishes
&lt;/h2&gt;

&lt;p&gt;The selection rules I arrived at:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A metric has to be readable from a live system&lt;/strong&gt; — from monitoring, the tracker, the app store. A metric the lead counts by hand at the end of the month will be dead within a quarter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thresholds are set from facts, not from dreams.&lt;/strong&gt; Before writing down a response-time threshold, I pulled the real percentiles out of monitoring. It turned out that almost all the services were already inside the corridor and one stood out — and the threshold became honest: "pull up the outlier," not "everyone improve everything." A threshold taken from thin air demotivates worse than no threshold at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Few metrics.&lt;/strong&gt; Three or four standing plus three or four monthly per team is the ceiling. Past that, the evaluation system costs more than it's worth.&lt;/li&gt;
&lt;li&gt;Examples of "system → its metrics" pairs by domain: QA — the currency of the regression suite and the trend of defects escaping to production; infrastructure — recovery time and the share of alerts that actually get a response (an alert everyone ignores does more harm than no alert at all); backend — SLOs and error-budget burn; the mobile client — crash-free and ANR, whose thresholds the app store sets for you.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The stabilization pattern: when the goal is far away
&lt;/h2&gt;

&lt;p&gt;The most common reason KPIs "don't land": the goal is objectively unreachable this month. Between the current availability level and the target one there isn't a step but several quarters of work. Making the target level the monthly bar means condemning the lead to an "unsatisfactory" — and to cynicism.&lt;/p&gt;

&lt;p&gt;The pattern that works is &lt;strong&gt;burn-down plus a ladder toward the horizon&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;the absolute goal is a reference point&lt;/strong&gt;, not a monthly bar: it's honestly written down as the horizon;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;the monthly bar is a step&lt;/strong&gt;: noticeably reduce the number of errors, close their main source, climb one rung of the ladder you laid out from the current level to the horizon;&lt;/li&gt;
&lt;li&gt;what gets evaluated is the step: taken — "good," even if the horizon is still far away.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is also the answer to "how do we agree": the conversation with the lead stops being a haggle about the unreachable and becomes planning for the next rung.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to present it and how to agree
&lt;/h2&gt;

&lt;p&gt;The procedural part is short, but everything depends on it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Warn in advance.&lt;/strong&gt; The leads knew from ordinary conversations, long before the first card, that KPIs were being prepared and that we'd sit down to discuss them soon. It's a cheap step that removes half the tension: the system doesn't fall on them as a surprise, it arrives at an expected moment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't hand down a finished thing.&lt;/strong&gt; The frame (S/M, the levels, the pattern) is brought by the CTO; filling the card in is the subject of a conversation with the lead. Thresholds are discussed from the actual numbers, out loud, one on one. This is exactly the &lt;a href="https://mind.mansur.expert/en/people-bus-factor-expectations-and-day-one/" rel="noopener noreferrer"&gt;spoken expectations&lt;/a&gt;, in both directions — the lead also says what they need in order for the system to work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The CTO sets the goals of the month&lt;/strong&gt; — the final word is theirs, this isn't a democracy; but goals set without a conversation don't work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A monthly review&lt;/strong&gt; — by the card, half an hour: how the standing ones are doing, what happened to the goals of the month, what we set for the next one. A red metric is grounds for the question "what's in the way," not for a reprimand. One or two cycles in that mode and the fear of the stick goes away.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be careful with money.&lt;/strong&gt; Tying this to a bonus from the first month breaks the whole construction: a metric that has become money stops being a measurement instantly — that's the same Goodhart from the opening, only in his fastest form. First a quarter or two of simply keeping the rhythm and building trust in the numbers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What came out of it
&lt;/h2&gt;

&lt;p&gt;Not once did I hear a flat "no." That doesn't mean the cards were accepted in silence: every item was worked through one on one — and not once at launch, but continuously, at every review. That is the central point of the process itself: not to hand it down and forget it, but to talk and to agree until the person has their own understanding of why the metric is there. The absence of loud resistance is the result of those conversations, not a sign that nobody cared.&lt;/p&gt;

&lt;p&gt;There were corrections along the way, and always of one kind. Either a wording that could be read two ways, or a number that couldn't be reached this month. The first ones we rewrote. The second we replaced with tracking a trend: not "hold the threshold" but "move toward it" — with a check at the next review. Not once did we have to throw a metric out entirely: the argument was always about the threshold, never about the meaning.&lt;/p&gt;

&lt;p&gt;What changed noticeably:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ANRs and crashes became the leads' concern, not the CTO's.&lt;/strong&gt; Exactly what the whole thing was started for: the tracking settled on the teams' side — the lead looks at the numbers and arrives with a plan, instead of waiting for a question from above;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;QA stopped needing to be pulled in by hand.&lt;/strong&gt; Before, the lead had to be prodded both about the release process and about taking part in postmortems. Once the cards existed, the lead and the team started joining in on their own;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;responsibility stopped being a matter of guesswork.&lt;/strong&gt; It became visible who owns what — and that understanding became shared, rather than living "in the CTO's head".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don't have a case where the scheme wouldn't fit. There is a condition without which it doesn't work: agreeing in a way that leaves everyone understanding the goal. Without that, the cards turn into work for the sake of work — and it won't be the scheme's fault.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternatives — and when they're better
&lt;/h2&gt;

&lt;p&gt;An honest section: lead KPIs are not the only instrument.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OKRs&lt;/strong&gt; — if the task is not "keep the system healthy" but "move toward ambitious goals": inspiring objectives, measurable key results, 70% attainment is normal. Weaker for hygiene, stronger for pushes. Nothing stops you from combining them: the S part as KPIs, the M part in the spirit of OKRs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team health checks&lt;/strong&gt; (a regular self-assessment against a checklist: code, deployment, tests, mood) — softer, without thresholds, good as a quarterly reflection. Not enough if the C-level needs manageability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Just agreeing on expectations without numbers&lt;/strong&gt; — works in a small team with strong trust; stops working roughly when there are more than three leads and "keeping it in your head" no longer works.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The choice is simple: the more teams you have and the more expensive failures are, the closer you move to a formal system. But it's always worth starting with the conversation about expectations; KPIs are its written form, not a replacement for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checklist
&lt;/h2&gt;

&lt;p&gt;⬜ The lead owns the system, not the outcome; the outcome is a consequence&lt;br&gt;&lt;br&gt;
⬜ The frame: standing KPIs (hygiene) plus three or four goals of the month; a monthly review&lt;br&gt;&lt;br&gt;
⬜ Three levels of evaluation, through the maturity of the system, not through "red numbers"&lt;br&gt;&lt;br&gt;
⬜ Metrics are read from live systems, thresholds are grounded in facts&lt;br&gt;&lt;br&gt;
⬜ A distant goal is the horizon; the monthly bar is a step (burn-down / one rung)&lt;br&gt;&lt;br&gt;
⬜ The frame is brought by the CTO, the content is a conversation with the lead from actual numbers&lt;br&gt;&lt;br&gt;
⬜ A red metric is the question "what's in the way," not a reprimand&lt;br&gt;&lt;br&gt;
⬜ A number that's unreachable this month is replaced by a trend, not by an "unsatisfactory"&lt;br&gt;&lt;br&gt;
⬜ Don't tie money to the metrics until the system has lived through a couple of quarters&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>management</category>
      <category>productivity</category>
      <category>career</category>
    </item>
    <item>
      <title>People: Bus Factor, Expectations and Day One</title>
      <dc:creator>Mansur Fattakhov</dc:creator>
      <pubDate>Fri, 18 Sep 2026 11:20:01 +0000</pubDate>
      <link>https://dev.to/fattakhov/people-bus-factor-expectations-and-day-one-2hl4</link>
      <guid>https://dev.to/fattakhov/people-bus-factor-expectations-and-day-one-2hl4</guid>
      <description>&lt;p&gt;Part of the &lt;a href="https://mind.mansur.expert/en/how-to-bring-order-to-a-project-track-map/" rel="noopener noreferrer"&gt;"How to bring order to a project"&lt;/a&gt; series — the people track. It comes after infrastructure and architecture not because it matters less, but the opposite: it's the most important and the slowest track. A config can be moved into git in a day; trust, habits and the understanding of "who owns what" take months to rebuild.&lt;/p&gt;

&lt;p&gt;Let me repeat the track's thesis from the hub, because it's the main one: &lt;strong&gt;code with a bus factor of 1 is not an asset, it's a hostage.&lt;/strong&gt; And the other way around: a team where knowledge flows freely can fix any code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1. Bus factor: find the hostages
&lt;/h2&gt;

&lt;p&gt;The source material is the "who knows what" map from the &lt;a href="https://mind.mansur.expert/en/state-snapshot-first-week-on-a-new-project/" rel="noopener noreferrer"&gt;state snapshot&lt;/a&gt;. If the "who knows it" column shows the same name for five services, you have five hostages and one irreplaceable person — who, by the way, isn't enjoying it either: they can't take a vacation or move to new work.&lt;/p&gt;

&lt;p&gt;The treatment is three moves, in order of rising cost:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Documentation on touch&lt;/strong&gt; — the cheapest: figured out a piece — leave a passport or a note (this is already built into the &lt;a href="https://mind.mansur.expert/en/architecture-write-it-down-before-rewriting/" rel="noopener noreferrer"&gt;architecture track&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pairing on tasks&lt;/strong&gt; — a second person on tasks in a "solo" zone; slower, but the knowledge transfers live&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ownership rotation&lt;/strong&gt; — the most expensive, for critical zones: a service officially gets a second owner&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don't try to push the bus factor to two everywhere — life is too short. Prioritize by the risk map: first what is both critical and lives in one head.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2. Ownership: assemble from facts, don't draw it
&lt;/h2&gt;

&lt;p&gt;The classic mistake is to sit down and draw a beautiful responsibility matrix the way it "should be." A matrix like that dies within a week, because it doesn't match reality.&lt;/p&gt;

&lt;p&gt;The working recipe is to assemble it &lt;strong&gt;from facts&lt;/strong&gt;: pull half a year of closed tracker tasks and the git history of the repositories, and see who actually does the work and who actually decides. That's how our RACI matrix across all services was born: for each one — who executes (R), who's accountable for the result (A), who's consulted (C), who's informed (I). Not "as designed" but "as is" — with all the uncomfortable discoveries, like a service whose executor sits in one team while accountability sits nowhere in particular.&lt;/p&gt;

&lt;p&gt;From there the matrix works both ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a mismatch of "one does it, another answers for it" becomes a conversation and a decision instead of a silent conflict;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;every&lt;/strong&gt; service gets a name next to it — and that name comes from reality.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Updates happen on touch, like everything in this series: the owner changed — the row changes in the same merge request as the passport.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3. Delegate — and spell out expectations
&lt;/h2&gt;

&lt;p&gt;The most personal part of the track. When you're bringing order to a project, it's easy to fall into a trap: you know how to do it right, so you do everything important yourself. The outcome is predictable — you become the very bus factor of 1 you're fighting.&lt;/p&gt;

&lt;p&gt;You have to delegate. But delegation has an entry price that often goes unpaid: &lt;strong&gt;spelled-out expectations.&lt;/strong&gt; A familiar scenario: you hand over a task, the person works for a month, brings the result — and you expected something else. Nobody's guilty: you didn't say, they didn't ask, both filled the gaps with assumptions. A month burned, trust dented on both sides.&lt;/p&gt;

&lt;p&gt;What to spell out at handover — four things, literally as a list:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The result&lt;/strong&gt;: what "done" looks like — not the process, the end state. Better in writing, two sentences.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The deadline and checkpoints&lt;/strong&gt;: not "show me in a month," but "in a week we look at the direction." An early checkpoint is cheap insurance against "I expected something else": turning the work around after a week costs days; after a month it costs a month.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The boundaries of freedom&lt;/strong&gt;: what the person decides on their own, and what they bring to you. Without this they either ping you about everything, or silently make decisions at your level.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The why&lt;/strong&gt;: the task's context. A person who knows "why" makes the right micro-decisions where the instructions are silent.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And the symmetric rule: expectations are spelled out &lt;strong&gt;in both directions&lt;/strong&gt;. Ask what the person needs for the task to move — access, context, uninterrupted time. Half of the "didn't get it done" cases turn out, on review, to be "waited three weeks for access and was too shy to nag."&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4. Onboarding and one-on-ones
&lt;/h2&gt;

&lt;p&gt;Onboarding is not "handed over a laptop and added to the chats." Two metrics worth measuring it by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;environment in a day&lt;/strong&gt; — by the end of day one the person runs the project locally;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;a production task in a week&lt;/strong&gt; — within the first week they ship something real, however tiny.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For that to work, the onboarding documents must exist before the hire, and there are exactly three levels — no more needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;company&lt;/strong&gt; — what the product is, which teams exist, who leads them, where to look;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;engineering&lt;/strong&gt; — how we work: a task's lifecycle from discussion to deploy, the standards, the decision process;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;glossary&lt;/strong&gt; — the domain dictionary: internal terms and abbreviations "everybody just knows."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And separately — &lt;strong&gt;one-on-ones for the early period.&lt;/strong&gt; Regular short private meetings: weekly for the first month or two, less often later. Not a status update (the tracker exists for that), but "how is it going for you": what's confusing, what's in the way, what surprised you. In their first weeks a fresh person sees your oddities better than any veteran — then they get used to it and that vision is gone. One-on-ones are how you collect it in time. They're also the natural place to re-sync the expectations from step 3 — while a mismatch costs days, not months.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5. Hiring: an honest footnote
&lt;/h2&gt;

&lt;p&gt;Hiring gets one step in this plan, and a short one — not because the topic is small, but because it's a &lt;strong&gt;separate big one&lt;/strong&gt;. The market is full of people; picking the one who will strengthen your particular team is a discipline of its own that doesn't fit into a section.&lt;/p&gt;

&lt;p&gt;What does fit is one rule that costs little and gives a lot: &lt;strong&gt;write the job post from the project's real tasks, not from a list of technologies.&lt;/strong&gt; Not "Python, Docker, K8s, 5 years of experience," but "here are three tasks you'll work on in your first quarter." A text like that filters by itself: the people who respond are the ones interested in the tasks, not the ones who matched the keywords. And the interview gets a ready-made script — you talk about those three tasks.&lt;/p&gt;

&lt;p&gt;The rest — the funnel, evaluation, probation — deserves its own article; it's in the backlog.&lt;/p&gt;

&lt;h2&gt;
  
  
  What came out of it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The irreplaceable stopped being irreplaceable — and breathed easier themselves&lt;/li&gt;
&lt;li&gt;Every service has a name from reality, not from the org chart&lt;/li&gt;
&lt;li&gt;Delegated tasks arrive at the intended result — because the intended result was spelled out up front&lt;/li&gt;
&lt;li&gt;A new person is useful in their first week, and their fresh perspective is collected, not lost&lt;/li&gt;
&lt;li&gt;Job posts answer "what will I actually do here," and that's who applies&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The track's checklist
&lt;/h2&gt;

&lt;p&gt;⬜ Bus factor from the snapshot map; treatment by cost: docs → pairing → rotation&lt;br&gt;&lt;br&gt;
⬜ Responsibility matrix assembled from facts (tracker + git), not drawn&lt;br&gt;&lt;br&gt;
⬜ Every service has a name; "does vs. answers" mismatches resolved&lt;br&gt;&lt;br&gt;
⬜ Delegation = result + checkpoints + boundaries of freedom + the "why"&lt;br&gt;&lt;br&gt;
⬜ Expectations spelled out both ways, in writing&lt;br&gt;&lt;br&gt;
⬜ Onboarding: environment in a day, a production task in a week&lt;br&gt;&lt;br&gt;
⬜ Three documents: company / engineering / glossary — existing before the hire&lt;br&gt;&lt;br&gt;
⬜ One-on-ones early on: weekly for the first months, not status but "how is it going"&lt;br&gt;&lt;br&gt;
⬜ Job posts — from real tasks, not from a list of technologies&lt;/p&gt;

&lt;p&gt;The other tracks of the series are on the &lt;a href="https://mind.mansur.expert/en/how-to-bring-order-to-a-project-track-map/" rel="noopener noreferrer"&gt;map&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://mind.mansur.expert/en/people-bus-factor-expectations-and-day-one/" rel="noopener noreferrer"&gt;mind.mansur.expert&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>management</category>
      <category>teamwork</category>
      <category>career</category>
    </item>
    <item>
      <title>Architecture: Write It Down Before Rewriting</title>
      <dc:creator>Mansur Fattakhov</dc:creator>
      <pubDate>Wed, 16 Sep 2026 07:13:17 +0000</pubDate>
      <link>https://dev.to/fattakhov/architecture-write-it-down-before-rewriting-2lcl</link>
      <guid>https://dev.to/fattakhov/architecture-write-it-down-before-rewriting-2lcl</guid>
      <description>&lt;p&gt;Part of the &lt;a href="https://mind.mansur.expert/en/how-to-bring-order-to-a-project-track-map/" rel="noopener noreferrer"&gt;"How to bring order to a project"&lt;/a&gt; series — the architecture track. Same context: a growing gamedev project, about twenty services on different stacks, an architecture that "grew historically." Nobody designed it badly — nobody designed it at all: services appeared as needs arose, boundaries were drawn by circumstance, decisions lived in chats and heads.&lt;/p&gt;

&lt;p&gt;That's a normal phase — up to a point. The signals that the point has arrived:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;incidents increasingly happen &lt;strong&gt;at the seams&lt;/strong&gt; between services, not inside them;&lt;/li&gt;
&lt;li&gt;every change starts with an argument about "whose area is this anyway";&lt;/li&gt;
&lt;li&gt;estimates keep growing, because "we need to be careful there, we don't know what we might break";&lt;/li&gt;
&lt;li&gt;the only answer to "why is it built this way?" is "historically."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The track's main thesis: &lt;strong&gt;order in architecture starts not with rewriting, but with writing down.&lt;/strong&gt; Half of the "architecture problems" dissolve once the system is honestly described as it is. The other half turns from "scary to touch" into tasks with a price and a plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1. Service passports
&lt;/h2&gt;

&lt;p&gt;The first tool is boring to the point of indecency: every service gets a passport. One page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# &amp;lt;service&amp;gt;
Why it exists: one sentence.
Area of responsibility: what it does.
Boundaries: what it does NOT do, even when "it would be convenient here."
Stack, port, status (production / deprecated / experiment).
Owner: a name, not "the team in general."
Operations: how it deploys, where the configs are, where it logs.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three rules that keep passports alive instead of becoming a wiki graveyard:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The owner writes it, not the architect.&lt;/strong&gt; A passport is "I'm responsible for this and here's how it works," not "management told me to document it."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Half an hour, no more.&lt;/strong&gt; A passport is not design documentation. If it takes a day, you're writing something else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Updated on touch.&lt;/strong&gt; Changed the boundaries or the deploy method — update the passport in the same merge request. Dedicated "documentation days" don't exist; they don't work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Filling in the passports is diagnostics in itself: you keep catching moments of "wait, why is this even here?" — and those are the right questions, there was just nobody to ask them before. A couple of our services honestly moved to deprecated status during passportization — it turned out habit was the only thing keeping them alive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2. The system map
&lt;/h2&gt;

&lt;p&gt;The passports add up to a map — two artifacts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The registry&lt;/strong&gt; — a table of all services: type, stack, status, owner. Boring, but this is where you see that there aren't "about fifteen" services, but exactly nineteen, three of which are alive only in legends.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The call graph&lt;/strong&gt; — in text, per service: whom it calls, over which protocol, and why. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;shop → accounts service    HTTP REST   ownership check at purchase
shop → game API            HTTP REST   progress levels (callback)
shop ← payment webhooks    HTTPS       events from payment providers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No pretty arrows at the start — a table is enough. And it has a property a slide-deck diagram never will: it lives in git next to the code, so it can be reviewed and diffed. Changed an integration — the map's row changes in the same merge request; a mismatch is caught in review, not a year later.&lt;/p&gt;

&lt;p&gt;The rule is the same as in the &lt;a href="https://mind.mansur.expert/en/state-snapshot-first-week-on-a-new-project/" rel="noopener noreferrer"&gt;state snapshot&lt;/a&gt;: trust reality, not documentation. A map that shows the desired state is worse than no map — people make wrong decisions with full confidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3. Boundaries: where a service ends
&lt;/h2&gt;

&lt;p&gt;The most expensive architecture bugs live not inside services but between them — where two services consider the same thing "theirs." The signs of broken boundaries are recognizable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;two services write to the same database table;&lt;/li&gt;
&lt;li&gt;the same business logic is duplicated in two places "for speed";&lt;/li&gt;
&lt;li&gt;a service reaches into a neighbor's internals past its API, "because it's simpler";&lt;/li&gt;
&lt;li&gt;the first half hour of every incident goes into figuring out whose area it is.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our classic was the payment flow: historically payments started in the accounts service, then the shop grew its own purchases and webhooks — and the logic sprawled across two homes. Every payment incident opened with archaeology: which of the two services was supposed to handle this event? Duplicated handlers masked the problem right until the day they both handled the same event.&lt;/p&gt;

&lt;p&gt;The cure is a boundaries document. For every service, explicitly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;inside&lt;/strong&gt;: what the service is solely responsible for&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;outside&lt;/strong&gt;: what's adjacent but belongs to someone else (and to whom exactly)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;interface&lt;/strong&gt;: how others interact with it — and that's the only door&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The order of the fix matters: &lt;strong&gt;document first, code second.&lt;/strong&gt; First the team agrees on paper where payment events live (in our case — webhooks in the shop, accounts only read status via API), and only then the code is brought to the agreement. The reverse doesn't work: code without an agreement sprawls back within a quarter.&lt;/p&gt;

&lt;p&gt;The goal isn't beauty but two practical properties: &lt;strong&gt;no overlaps&lt;/strong&gt; (every entity has one owner) and &lt;strong&gt;growth control&lt;/strong&gt; (a new feature first gets an address — which service it goes to and why — instead of settling wherever).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4. Decisions on paper
&lt;/h2&gt;

&lt;p&gt;Architecture "in heads" dies when people leave. Two document genres stuck with us, and the difference between them matters:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RFC — before a change.&lt;/strong&gt; Half a page, by template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Problem: what hurts, in numbers or incidents.
Options: 2–3, including "do nothing."
Decision: which one and why.
Cost: work, risks, what about rollback.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The lifecycle is simple: Draft → Review → Approved/Rejected → Implemented. A new RFC is automatically announced in the team channel — the review is open, and objections at the Draft stage cost one comment instead of a production rework. A live example: the proposal to replace direct calls between game services with a message bus went through three review iterations — and the final design (with a journal and delivery guarantees) had almost nothing in common with the first draft. All those iterations happened &lt;strong&gt;before&lt;/strong&gt; the first line of code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ADRs and research — around a decision.&lt;/strong&gt; A short record: what was decided, why, what was considered and rejected. Plus research for specific risks — "how to back up the analytics database," "the plan for moving data off the game host." The difference from an RFC: nothing is being proposed here; knowledge is being recorded — so the same research doesn't get redone a year later.&lt;/p&gt;

&lt;p&gt;And one rule on top of both genres: &lt;strong&gt;before any architecture work — check the index first.&lt;/strong&gt; Don't reinvent what's already decided; don't start a migration if there's an open document about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5. Tech debt with a price tag
&lt;/h2&gt;

&lt;p&gt;Everyone has a tech debt list; it only works with two extra columns:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Debt&lt;/th&gt;
&lt;th&gt;What it blocks&lt;/th&gt;
&lt;th&gt;Cost to fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Shop and accounts share payment logic&lt;/td&gt;
&lt;td&gt;every payment incident ×2 in time; new providers go into two places&lt;/td&gt;
&lt;td&gt;~3 weeks: boundaries + moving the webhooks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No backups for the analytics DB&lt;/td&gt;
&lt;td&gt;losing all analytics on a disk failure&lt;/td&gt;
&lt;td&gt;research done, ~1 week to implement&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;"Blocks" is the main column. Debt that blocks nothing is not debt — it's a trait of the system; cross it out and stop carrying it. Debt that makes every feature in a service cost ×2 is the first candidate for work — and now that's visible not only to the tech lead, but to the product manager who sets priorities.&lt;/p&gt;

&lt;p&gt;The list's sources are the &lt;a href="https://mind.mansur.expert/en/state-snapshot-first-week-on-a-new-project/" rel="noopener noreferrer"&gt;state snapshot&lt;/a&gt; and incident reviews. Debt is paid down two ways: small — by the touch rule (touching a service — leave it cleaner), large — through an RFC, like any architecture change. Once a quarter the list is revised: some items closed by touch, some stopped blocking, some grew into an RFC.&lt;/p&gt;

&lt;h2&gt;
  
  
  How not to slide into bureaucracy
&lt;/h2&gt;

&lt;p&gt;The honest risk of this track: getting carried away and demanding an RFC for renaming a variable. The antidote is explicit thresholds. For us, an RFC is mandatory when a change:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;moves a &lt;strong&gt;boundary&lt;/strong&gt; between services (or creates a new service);&lt;/li&gt;
&lt;li&gt;touches &lt;strong&gt;data&lt;/strong&gt;: a shared DB schema, a migration, a storage change;&lt;/li&gt;
&lt;li&gt;touches &lt;strong&gt;money&lt;/strong&gt;: payment flows, billing;&lt;/li&gt;
&lt;li&gt;is irreversible or expensive to roll back.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything else is a regular merge request with a regular review. And the second antidote: all documents live next to the code or in one wiki with a single index, get updated on touch, and fit on a page. A document nobody can find or finish reading — that's what bureaucracy is.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it buys you: evolution without revolution
&lt;/h2&gt;

&lt;p&gt;With the map, the boundaries and the decision process in place, big rebuilds stop being leaps of faith. From the same project's practice — three rebuilds, all through RFCs and along the boundaries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The most loaded service was rewritten in another language&lt;/strong&gt; — entirely behind its own boundary. The API contract was pinned in the passport and the boundaries document, so "rewrite the internals" became a local task: the other seventeen services noticed nothing. Without a pinned contract this would have been "rewriting the system."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The shop was extracted from the "main API" that did everything&lt;/strong&gt; — first the boundaries document got the line "purchases, cases, storefront — a separate service," then the code moved along that boundary. Extraction by document rather than intuition is also a ready checklist: what moves, what stays, where the interface is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data was separated from compute onto different hosts&lt;/strong&gt; — following a research document with a migration plan, in stages, with rollback points. Not "overnight on Saturday," but a boring sequence of steps, each of which could be stopped.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these rebuilds was a "big rewrite." Each was a local operation with a clear price and a rollback. That's the point of the track: &lt;strong&gt;architectural order is not a pretty diagram on the wall — it's cheap changes.&lt;/strong&gt; The diagram is a by-product.&lt;/p&gt;

&lt;h2&gt;
  
  
  The track's checklist
&lt;/h2&gt;

&lt;p&gt;⬜ A passport for every service: why, area, boundaries, owner, operations&lt;br&gt;&lt;br&gt;
⬜ Owners write the passports; updates on touch, in the same merge request&lt;br&gt;&lt;br&gt;
⬜ Service registry + call graph in text, in git; verified against reality&lt;br&gt;&lt;br&gt;
⬜ Boundaries document: inside / outside / interface; one owner per entity&lt;br&gt;&lt;br&gt;
⬜ Fixing boundaries: agreement on paper first, code second&lt;br&gt;&lt;br&gt;
⬜ RFC before a change (problem → options → decision → cost); open review in the channel&lt;br&gt;&lt;br&gt;
⬜ Explicit RFC thresholds: boundaries, data, money, irreversibility — the rest is a regular MR&lt;br&gt;&lt;br&gt;
⬜ ADRs and research: record knowledge, "index first"&lt;br&gt;&lt;br&gt;
⬜ Tech debt with "what it blocks" and "cost" columns; quarterly revision&lt;br&gt;&lt;br&gt;
⬜ Small debt — by touch; large — through an RFC&lt;/p&gt;

&lt;p&gt;The other tracks of the series are on the &lt;a href="https://mind.mansur.expert/en/how-to-bring-order-to-a-project-track-map/" rel="noopener noreferrer"&gt;map&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://mind.mansur.expert/en/architecture-write-it-down-before-rewriting/" rel="noopener noreferrer"&gt;mind.mansur.expert&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>documentation</category>
      <category>backend</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
