<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<title>Blog</title>
	<subtitle>The personal blog of Jean-François Moy.</subtitle>
	<link rel="self" type="application/atom+xml" href="https://www.moystard.com/blog/feed.xml"/>
  <link rel="alternate" type="text/html" href="https://www.moystard.com/blog/"/>
  
	<updated>2026-07-23T00:00:00+00:00</updated>
	
	<id>https://www.moystard.com/blog/feed.xml</id>
	<entry xml:lang="en">
		<title>Spending judgement where it matters: how we structure AI-native delivery</title>
		<published>2026-07-23T00:00:00+00:00</published>
		<updated>2026-07-23T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://www.moystard.com/blog/ai-native-delivery/"/>
		<id>https://www.moystard.com/blog/ai-native-delivery/</id>
    
    <summary>&lt;p&gt;A few weeks ago &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.moystard.com&#x2F;blog&#x2F;ai-cognitive-debt&quot;&gt;I wrote about cognitive debt&lt;&#x2F;a&gt;: the slow loss of judgement that comes from letting a model think in your place. The natural question that follows: how do you use these tools seriously, at full speed, without paying that price?&lt;&#x2F;p&gt;
&lt;p&gt;Here is my answer, after a year of building a production fintech this way. It isn&#x27;t using the tools less. It isn&#x27;t trying harder to stay alert, because attention will fade when deadlines loom closer. It&#x27;s structure: a delivery pipeline where human judgement focuses on the thinking and decisions that deserve it. Everything else is enforced by gates that don&#x27;t rely on anyone remembering to care.&lt;&#x2F;p&gt;</summary>
    
		<content type="html" xml:base="https://www.moystard.com/blog/ai-native-delivery/">&lt;p&gt;A few weeks ago &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.moystard.com&#x2F;blog&#x2F;ai-cognitive-debt&quot;&gt;I wrote about cognitive debt&lt;&#x2F;a&gt;: the slow loss of judgement that comes from letting a model think in your place. The natural question that follows: how do you use these tools seriously, at full speed, without paying that price?&lt;&#x2F;p&gt;
&lt;p&gt;Here is my answer, after a year of building a production fintech this way. It isn&#x27;t using the tools less. It isn&#x27;t trying harder to stay alert, because attention will fade when deadlines loom closer. It&#x27;s structure: a delivery pipeline where human judgement focuses on the thinking and decisions that deserve it. Everything else is enforced by gates that don&#x27;t rely on anyone remembering to care.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;&lt;h1 id=&quot;the-scarce-resource-has-changed&quot;&gt;The scarce resource has changed&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-scarce-resource-has-changed&quot; aria-label=&quot;Anchor link for: the-scarce-resource-has-changed&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h1&gt;
&lt;p&gt;For my whole career, engineering capacity was the constraint. Roadmaps were shaped around what the teams could carry. With agentic tooling, that constraint has clearly shifted: a small team can now produce what used to take a department.&lt;&#x2F;p&gt;
&lt;p&gt;What hasn&#x27;t moved is the supply of judgement. Someone still has to say: we are solving the wrong problem; this technical design will not scale and hurt us in a year; this edge case is the one that matters.&lt;&#x2F;p&gt;
&lt;p&gt;I think that is now the real job of an engineering leader. It is not adopting AI; everyone has. It is about deciding where human attention goes, and building the system that encourages it and protects it.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-initial-lag-can-fool-you&quot;&gt;The initial lag can fool you&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-initial-lag-can-fool-you&quot; aria-label=&quot;Anchor link for: the-initial-lag-can-fool-you&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h1&gt;
&lt;p&gt;The dangerous thing about uncontrolled usage is that it feels great. The tools are impressive, output pours out, velocity charts point up. Nothing seems wrong.&lt;&#x2F;p&gt;
&lt;p&gt;The bill comes later, and it compounds quietly: vicious bugs in code nobody truly read; confident implementations of acceptance criteria that were invalid, or misunderstood, because nobody challenged them before building; a team that no longer knows its own codebase, because everything was delegated and stamped. By the time the symptoms show, you don&#x27;t have a bug. You have an archaeology dig, run by people who have lost the fluency to dig.&lt;&#x2F;p&gt;
&lt;p&gt;Cognitive debt and technical debt end up collapsing into the same debt. The cost is simply deferred, invisible as it grows, and due all at once. &quot;It&#x27;s working fine so far&quot; is not evidence. This initial lag is the trap.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;what-the-structure-looks-like&quot;&gt;What the structure looks like&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-the-structure-looks-like&quot; aria-label=&quot;Anchor link for: what-the-structure-looks-like&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h1&gt;
&lt;p&gt;The pipeline our team runs replicates the roles of a delivery team, sequenced so work flows from ticket to implementation to released change, with a quality gate at each handoff. Every engineer runs it the same way, each owning the judgement calls on their own tickets. They are ultimately accountable for the outcome.&lt;&#x2F;p&gt;
&lt;p&gt;It starts before any code is written. Tickets are authored as user stories with acceptance criteria, then challenged: the system&#x27;s first job is to argue with the scope, highlight misalignment with the existing system, surface the gaps, and openly ask the contentious questions. In the meantime, the engineer shapes the ticket and the technical design. Anything diagnostic follows a data-first rule: metrics, logs and data records before hypotheses.&lt;&#x2F;p&gt;
&lt;p&gt;The implementation starts with a test-first approach against the acceptance criteria: the behaviour is pinned before a single line of production code exists. Once the implementation is complete, a suite of reviews runs in parallel, each an independent lens: one checks the change against the ticket acceptance criteria, one against the project&#x27;s architecture and conventions, one for API-contract stability, one for security and the supply chain, and finally, a simplification pass whose only job is to remove complexity that doesn&#x27;t pay rent. None of this depends on someone remembering to run it.&lt;&#x2F;p&gt;
&lt;p&gt;The final brick is the live validation: an agent runs the change against a real environment, driving the actual UI, checking database side effects, and checking logs. It walks the happy path, but also pokes at the unhappy ones. The acceptance criteria are proven on a running system, beyond code testing.&lt;&#x2F;p&gt;
&lt;p&gt;By that time, a rich PR is opened and merge-gated on clean re-review by a separate agent, green CI and explicit human sign-off.&lt;&#x2F;p&gt;
&lt;p&gt;Our &quot;kickoff&quot; command carries a ticket from scope-challenge to merged PR. During that time, human attention is spent at exactly two points: shaping the work at product and engineering level before it starts, and the contentious calls the gates escalate along the way. The tiny implementation details that used to eat whole review cycles never reach a person.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;why-this-keeps-judgement-sharp&quot;&gt;Why this keeps judgement sharp&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-this-keeps-judgement-sharp&quot; aria-label=&quot;Anchor link for: why-this-keeps-judgement-sharp&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h1&gt;
&lt;p&gt;Cognitive debt grows when you fully delegate your judgement and the model does your thinking. Your role is to stamp its output. Our methodology reverses that twice.&lt;&#x2F;p&gt;
&lt;p&gt;First, it concentrates the thinking that stays human. Nobody reviews boilerplate or formatting, so the judgement each engineer does exercise, on scope, design and the genuine hard calls, gets full attention. The skill you keep is the skill you use.&lt;&#x2F;p&gt;
&lt;p&gt;Second, the gates are structural, not optional. Speed and safety stop competing the moment quality checks are steps the next stage depends on, rather than qualities someone must remember to practise. A pipeline that cannot merge without independent review, passing tests and live validation doesn&#x27;t get lazier as the people get faster. This rigour pays off.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s a compounding layer too. The system remembers the team&#x27;s conventions; lessons are taught and documented once. Preferences become guardrails instead of tribal knowledge. Every mistake and inefficiency becomes a rule, exactly once.&lt;&#x2F;p&gt;
&lt;p&gt;These gates do not slow us down: they spend machine time, not human attention, running in parallel while we shape the next piece of work. Working this way, three engineers took a new product from concept to public launch in six weeks, on a platform that processes millions of financial events a month, in software where a wrong number in front of a customer is unacceptable. I don&#x27;t believe that pace, at that bar, is reachable for a team this size any other way.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;what-it-costs&quot;&gt;What it costs&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-it-costs&quot; aria-label=&quot;Anchor link for: what-it-costs&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h1&gt;
&lt;p&gt;None of this is free. The harness took real time to build and tune; the gates encode our conventions and, more importantly, our architecture, and encoding them was work. That last point matters: the codebase has a purposeful shape, decided by people, and the pipeline defends it on every change. A model without that constraint will happily grow you a system with no shape at all. The whole system lives or dies on well-formed acceptance criteria: feed it vague or ambiguous scope and it will confidently build the wrong thing. That&#x27;s why the scope-challenge stage exists, and why it&#x27;s the one stage I would never automate away. The live-validation environment is infrastructure that needs an owner, like any other.&lt;&#x2F;p&gt;
&lt;p&gt;And one problem I won&#x27;t pretend to have solved: this model assumes seasoned judgement at the gates. How a junior grows that judgement inside a system that shields them from the grind that used to build it is, I think, the hardest problem in this new AI-era engineering leadership. This is a problem we need to collectively tackle: the future of our discipline and our companies depends on this new generation of software engineers developing their judgement.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;a-message-to-engineering-leaders-and-engineers-out-there&quot;&gt;A message to engineering leaders and engineers out there&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-message-to-engineering-leaders-and-engineers-out-there&quot; aria-label=&quot;Anchor link for: a-message-to-engineering-leaders-and-engineers-out-there&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h1&gt;
&lt;p&gt;Adoption of agentic programming is not a question any more; the tools are everywhere, and their impact is undeniable. The divide that matters is between teams that use them under control and teams quietly accumulating the lag-hidden debt above while their dashboards look great. The second group may not feel wrong yet. That&#x27;s the point.&lt;&#x2F;p&gt;
&lt;p&gt;For leaders, the path that works takes more design effort than either enthusiasm or caution. Decide which decisions deserve human judgement in your context and domain. Build the structure that routes those decisions to people. Make every other quality property a gate. The result is the combination everyone assumes is a trade-off: faster delivery, less debt, and engineers whose judgement sharpens with use, because it&#x27;s the only thing they spend all day exercising.&lt;&#x2F;p&gt;
&lt;p&gt;For engineers, the gates are just skills: plain files that encode what your team knows (the domain, the conventions, the architecture, the strategy behind the codebase). Anyone can write one. Champion the framework, own it, improve it, add the rule you would have needed yesterday. It isn&#x27;t your manager&#x27;s system; it&#x27;s the team&#x27;s shared memory, and everyone can contribute to it.&lt;&#x2F;p&gt;
&lt;p&gt;One boundary worth stating plainly: everything here is about building things right. Whether you&#x27;re building the right things is a different discipline, and no pipeline answers it for you.&lt;&#x2F;p&gt;
&lt;p&gt;As the tools keep improving, the structure decides whether they make your team more capable or just more prolific.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>We are trading productivity for cognitive debt</title>
		<published>2026-06-25T00:00:00+00:00</published>
		<updated>2026-06-25T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://www.moystard.com/blog/ai-cognitive-debt/"/>
		<id>https://www.moystard.com/blog/ai-cognitive-debt/</id>
    
    <summary>&lt;p&gt;We are trading productivity for cognitive debt, and most of us have not noticed the bill.&lt;&#x2F;p&gt;</summary>
    
		<content type="html" xml:base="https://www.moystard.com/blog/ai-cognitive-debt/">&lt;p&gt;We are trading productivity for cognitive debt, and most of us have not noticed the bill.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;I have been thinking about this a lot as the recent models have grown genuinely impressive. The MIT Media Lab put a name to it this year: writers who leaned on LLMs showed measurably lower neural engagement. Cognitive debt.&lt;&#x2F;p&gt;
&lt;p&gt;For software engineers and architects, the choice between deep and shallow understanding is not new. It used to be StackOverflow or a blog post, copy-pasted in haste, versus the slower work of understanding the problem space well enough to build something strong and context-aware.&lt;&#x2F;p&gt;
&lt;p&gt;LLMs raise the same choice, but with a twist: their solutions always look credible and optimal. Only a seasoned engineer can specify a problem properly and judge a solution against the criteria that are specific to their company, domain, project and timing.&lt;&#x2F;p&gt;
&lt;p&gt;And as we hand more of that thinking to the machine, the skill quietly erodes. Not overnight, but through a slow loss of comprehension, tooling instinct and programmatic and design fluency that is hard to notice until you need it and find it gone.&lt;&#x2F;p&gt;
&lt;p&gt;So the reminder I keep giving myself: think, evaluate, challenge whatever you introduce into your systems. Keep the mind sharp and the decisions deliberate.&lt;&#x2F;p&gt;
&lt;p&gt;This post was inspired by a great read from Igor Roztropiński: &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;binaryigor.com&#x2F;the-joy-and-power-of-understanding.html&quot;&gt;The Joy and Power of Understanding&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Follow-up: &lt;a href=&quot;&#x2F;blog&#x2F;ai-native-delivery&quot;&gt;Spending judgement where it matters: how we structure AI-native delivery&lt;&#x2F;a&gt;.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Extra robustness in your Koa projects with Typescript</title>
		<published>2021-02-05T00:00:00+00:00</published>
		<updated>2021-02-05T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://www.moystard.com/blog/koa-typings/"/>
		<id>https://www.moystard.com/blog/koa-typings/</id>
    
    <summary>&lt;p&gt;With a bit of Typescript magic, it is possible to add strict typing to the body of Koa requests and responses.&lt;&#x2F;p&gt;</summary>
    
		<content type="html" xml:base="https://www.moystard.com/blog/koa-typings/">&lt;p&gt;With a bit of Typescript magic, it is possible to add strict typing to the body of Koa requests and responses.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;Typescript and Koa users know how loose the Typescript typing is for the requests and responses of the Koa contexts. Indeed, Koa (and &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dlau&#x2F;koa-body&quot;&gt;Koa-Body&lt;&#x2F;a&gt;) default types use &lt;code&gt;any&lt;&#x2F;code&gt; for the request and response body type.&lt;&#x2F;p&gt;
&lt;p&gt;This short blog post will show how you can add more robustness to your controllers by typing your bodies thanks to a simple generic interface.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;sparkling-koa-with-genericity&quot;&gt;Sparkling Koa with Genericity&lt;a class=&quot;zola-anchor&quot; href=&quot;#sparkling-koa-with-genericity&quot; aria-label=&quot;Anchor link for: sparkling-koa-with-genericity&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h1&gt;
&lt;p&gt;We want to be able to provide the types of the request and the response body, whilst keeping the built-in flexibility. That&#x27;s easy to achieve with Typescript generics as demonstrated below:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; Context&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; Request&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; from&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;koa&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; KoaRequest&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;RequestBody&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; any&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt; Request&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    body&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;?&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; RequestBody&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;export&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; interface&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; KoaContext&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;RequestBody&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; any&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; ResponseBody&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; any&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt; Context&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    request&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; KoaRequest&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;RequestBody&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    body&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; ResponseBody&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;export&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; interface&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; KoaResponseContext&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ResponseBody&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt; KoaContext&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; ResponseBody&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Having typed bodies should not prevent you from runtime checking the request bodies to ensure that you were provided with a valid input. If you are composing middlewares, you probably runtime check up the chain, and having typed bodies downstream where you know the input has been validated prevents you from duplicating the checks.&lt;&#x2F;p&gt;
&lt;p&gt;For assembling responses, having type safety at the compiler level should prove enough.&lt;&#x2F;p&gt;
&lt;p&gt;I am actually surprised that those are not the original type definitions in &lt;code&gt;@types&#x2F;koa&lt;&#x2F;code&gt; and &lt;code&gt;@types&#x2F;koa-body&lt;&#x2F;code&gt; but I am sure they had their reasons.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;examples&quot;&gt;Examples&lt;a class=&quot;zola-anchor&quot; href=&quot;#examples&quot; aria-label=&quot;Anchor link for: examples&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s put our new interfaces in practice with a couple of examples.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; Router&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; from&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;koa-router&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; koaBody&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; from&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;koa-body&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; HelloWorldRequestDto&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    name&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; HelloWorldResponseDto&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    message&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; helloRouter&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; new&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Router&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;helloRouter&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; koaBody&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt; async&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; KoaContext&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;HelloWorldRequestDto&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; HelloWorldResponseDto&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Request body is now typed.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    const&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; name&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; ctx&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;res&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    ctx&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;res&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;statusCode&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 200&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Response body is also typed.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    ctx&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;body&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        message&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; `&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Hello &lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;!&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The compiler will error if we try to access properties not defined in the body types. Using the same DTO types, the code below would not compile:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;helloRouter&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; koaBody&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt; async&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; KoaContext&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;HelloWorldRequestDto&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; HelloWorldResponseDto&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; The compiler will fail as lastName is not defined in `HelloWorldRequestDto`.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    const&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; name&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant&quot;&gt; lastName&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; ctx&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;res&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    ctx&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;res&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;statusCode&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 200&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; The compiler would also fail here because we are defining a property absent from `HelloWorldResponseDto`, and `message` is absent.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    ctx&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;body&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        msg&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; `&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Hello &lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;!&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;KoaContext&lt;&#x2F;code&gt; interface uses default value for the generics; this allows specifying the request body type without the response&#x27;s: &lt;code&gt;KoaContext&amp;lt;RequestBodyType&amp;gt;&lt;&#x2F;code&gt; will assume that the response body type is &lt;code&gt;any&lt;&#x2F;code&gt;. The &lt;code&gt;KoaResponseContext&lt;&#x2F;code&gt; allows you to specify the response body type without the request&#x27;s for extra convenience.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;Leveraging Typescript and generics, we were able to add stricter typing to Koa. This should help you when composing middlewares, and assembling your response bodies. Happy coding!&lt;&#x2F;p&gt;
</content>
	</entry>
</feed>
