<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>software engineering &#8211; Rafael Bernard Araujo</title>
	<atom:link href="https://rafael.bernard-araujo.com/tag/software-engineering/feed" rel="self" type="application/rss+xml" />
	<link>https://rafael.bernard-araujo.com</link>
	<description>desenvolvendo... while(!success){  try(); }</description>
	<lastBuildDate>Wed, 09 Sep 2026 05:54:23 +0000</lastBuildDate>
	<language>pt-BR</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	
<site xmlns="com-wordpress:feed-additions:1">21941730</site>	<item>
		<title>The Principle Is Not the Mechanism: ISP, DIP, and Language Structures in Practice</title>
		<link>https://rafael.bernard-araujo.com/the-principle-is-not-the-mechanism-isp-dip-and-language-structures-in-practice.php</link>
					<comments>https://rafael.bernard-araujo.com/the-principle-is-not-the-mechanism-isp-dip-and-language-structures-in-practice.php#respond</comments>
		
		<dc:creator><![CDATA[rafael]]></dc:creator>
		<pubDate>Wed, 09 Sep 2026 05:54:06 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[dependency-injection]]></category>
		<category><![CDATA[design principles]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[software engineering]]></category>
		<category><![CDATA[SOLID]]></category>
		<category><![CDATA[structural-typing]]></category>
		<category><![CDATA[typescript]]></category>
		<guid isPermaLink="false">https://rafael.bernard-araujo.com/?p=2420</guid>

					<description><![CDATA[A small refactor made me think: how many times do we mix a principle with mechanisms? For example, Interface Segregation and Dependency Inversion are many times seen naturally in OOP, but they are principles and they survive a change of paradigm. TypeScript is this interesting case: it supports both OOP and functional styles in the [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><em>A small refactor made me think: how many times do we mix a principle with mechanisms? For example, Interface Segregation and Dependency Inversion are many times seen naturally in OOP, but they are principles and they survive a change of paradigm. TypeScript is this interesting case: it supports both OOP and functional styles in the same codebase, and both can preserve the I and D from SOLID. This article separates the principles from the mechanisms that carry them, maps how OOP and functional TypeScript preserve the same design responsibilities. And it proposes yet another comparison of things that look tied together, but are separate concepts in the language structures underneath: where structural typing and duck typing share a philosophy but diverge in practice.</em></p>
<h2>Contents</h2>
<ol>
<li>The in-memory repository that made the mapping visible</li>
<li>Preserve the responsibilities, change the mechanisms</li>
<li>Map responsibilities, not syntax</li>
<li>Service factories, closures, and composition</li>
<li>Structural typing: the conformance mechanism</li>
<li>Testing: where duck typing walks back in</li>
<li>Untangling the five concepts</li>
<li>The language field guide</li>
<li>A good way to use them</li>
<li>The honest counterpoint</li>
<li>Checklist</li>
<li>Recap</li>
</ol>
<p><em>Obs.: Check the Glossary at the end if you need clarification on concepts I am using in the article.</em></p>
<h2>The in-memory repository that made the mapping visible</h2>
<p>In a production TypeScript API I work on, the persistence design was still evolving while the Use Cases needed to move forward. We deliberately introduced an in-memory implementation of the domain contract:</p>
<pre><code class="language-typescript">// infrastructure/repository/in-memory.user.repository.ts
import { randomUUID } from &#039;node:crypto&#039;;
import type { NewUser, User, UserRepository } from &#039;../../domain/user&#039;;

const usersByDocument = new Map&lt;string, User&gt;();

async function findByDocument(document: string): Promise&lt;User | null&gt; {
  return usersByDocument.get(document) ?? null;
}

async function create(data: NewUser): Promise&lt;User&gt; {
  const user: User = { id: randomUUID(), ...data, createdAt: new Date() };
  usersByDocument.set(data.document, user);
  return user;
}

export const inMemoryUserRepository: UserRepository = { findByDocument, create };</code></pre>
<p>The services that depended on <code>UserRepository</code> received it as a parameter:</p>
<pre><code class="language-typescript">export async function register(
  input: RegisterInput,
  users: UserRepository,
): Promise&lt;User&gt; {
  const existing = await users.findByDocument(input.document);
  // ...
  return users.create(input);
}</code></pre>
<p>At the call site, the temporary seam was explicit:</p>
<pre><code class="language-typescript">await register(input, inMemoryUserRepository);</code></pre>
<p>That parameter was deliberate scaffolding. It allowed the Use Cases to progress against the contract while making the temporary in-memory choice visible wherever the service was called.</p>
<p>Later, the Postgres implementation of <code>UserRepository</code> became available. We began removing the extra repository parameter and moving toward an agreed convention: import the repository and use it inside the service. That convention itself would become part of the question.</p>
<pre><code class="language-ts">import { postgresUserRepository } from &#039;../infrastructure/repository/postgres.user.repository&#039;;

export async function register(input: RegisterInput): Promise&lt;User&gt; {
  const existing = await postgresUserRepository.findByDocument(input.document);
  // ...
  return postgresUserRepository.create(input);
}</code></pre>
<p>That was the direction of the refactor, not the conclusion of the design. It removed the temporary parameter and restored consistency with the surrounding code, but it also moved the implementation choice directly into the service and removed the visible substitution boundary. In PHP, I knew exactly where each responsibility lived. In TypeScript, the functional style I was working in didn't use those mechanisms.</p>
<p>In the OOP style, each responsibility had an obvious home: a small interface supported Interface Segregation; constructor injection supported Dependency Inversion; a Service Container selected implementations and assembled the graph; tests supplied another implementation of the same interface. The direct-import style looked different enough to raise the real question:</p>
<blockquote>
<p>Am I giving up Interface Segregation, Dependency Inversion, centralised composition, or isolated testing? Is functional TypeScript unsuitable for these principles, or am I looking for their OOP mechanisms instead of their functional equivalents? Should this service become a class, or can the functional style preserve the same design qualities from implementation through testing?</p>
</blockquote>
<p>The refactor did not reveal a missing principle. It revealed a comparison worth making: for every responsibility that had an obvious home in the OOP style, what technique (and what language structure) gave it an equivalent home in the functional style?</p>
<p>That comparison turned out to be a useful exercise in itself. Not just which pattern to use, but what is a principle, what is a mechanism, and what is a language structure. Where each of those pieces actually plays its role. The rest of this article works through that distinction, one responsibility at a time.</p>
<h2>Preserve the responsibilities, change the mechanisms</h2>
<p>The comparison becomes clearer when the creation of one service is separated from the composition of the whole application:</p>
<table>
<thead>
<tr>
<th>Design responsibility</th>
<th>Common OOP mechanism</th>
<th>Functional TypeScript mechanism</th>
</tr>
</thead>
<tbody>
<tr>
<td>Keep the consumer contract narrow</td>
<td>A small, consumer-specific interface</td>
<td>A small structural contract</td>
</tr>
<tr>
<td>Create one service instance</td>
<td>Invoke a class constructor directly or through a dedicated factory</td>
<td>Invoke a service factory such as <code>makeRegistrationService(...)</code></td>
</tr>
<tr>
<td>Retain injected dependencies</td>
<td>Private object fields</td>
<td>The returned functions' lexical closures</td>
</tr>
<tr>
<td>Compose the application graph</td>
<td>Invoke constructors/factories manually at the composition root, or use a Service Container</td>
<td>Invoke service factories manually at the composition root, or use a Service Container</td>
</tr>
<tr>
<td>Expose the wired application</td>
<td>The entry point receives or resolves a root service/application object</td>
<td>The composition function returns an application facade with already-wired operations</td>
</tr>
<tr>
<td>Test a consumer in isolation</td>
<td>Inject a fake/mock implementation through the constructor</td>
<td>Pass a structural or duck-typed double to the service factory</td>
</tr>
</tbody>
</table>
<p>This makes the factory equivalence explicit. <code>makeRegistrationService(users)</code> is doing two familiar jobs at once: its parameters are the functional counterpart of constructor injection, and invoking it is the functional counterpart of constructing the service through <code>new</code> or through a small OOP factory. The returned closure or object of functions is the service instance.</p>
<p>It also keeps <strong>composition</strong>, <strong>composition root</strong>, and <strong>Service Container</strong> distinct:</p>
<ul>
<li><strong>Composition</strong> is the activity of assembling the graph.</li>
<li>The <strong>composition root</strong> is the application location where that activity happens.</li>
<li>A <strong>Service Container</strong> is one optional mechanism for performing composition and resolution, often with lifecycle, scope, or discovery features. It can be used in either an OOP or functional application.</li>
<li>An explicit composition function is manual composition, not a complete replacement for every container capability.</li>
</ul>
<p>The composed result is another role again. The <code>application.register</code> operation on the returned <strong>application facade</strong> is a ready-to-use entry point into the wired graph. It is not a Service Container because callers are not resolving arbitrary services by token, and it is not managing service lifecycles. If application code asks a container for dependencies directly, that moves toward the Service Locator pattern rather than dependency injection.</p>
<p>None of these mechanisms creates a design principle by itself. A large interface still violates ISP. A constructor or factory typed to a concrete class still violates DIP. A container or composition function can centralise poor dependencies as efficiently as good ones. The value comes from assigning each responsibility deliberately.</p>
<p>Import/export syntax is not part of the comparison; both styles use ordinary module boundaries. The meaningful question is <strong>where the concrete implementation is selected</strong>. In the opening refactor, the Use Case selects <code>postgresUserRepository</code> itself and hard-wires the detail. In the mapped design, the composition root selects it and the service factory receives only the <code>UserRepository</code> contract.</p>
<p>The discovery wasn't any of the individual concepts, they were all familiar. It was the <strong>mapping between coding styles</strong>. How do OOP and functional TypeScript use different techniques and language structures to preserve Interface Segregation and Dependency Inversion, from implementation through isolated testing? Asking that comparative question put contracts, constructors, factories, closures, composition, containers, facades, and test doubles in their respective roles. The concepts stayed the same; the mechanisms changed.</p>
<p>That comparison opened a second layer: <strong>what structure in the language makes each technique possible?</strong> Class constructors and object fields create and retain dependencies in the OOP route. First-class functions and lexical closures do the equivalent work in the functional route, while ordinary function application composes the graph. TypeScript's structural type system checks contracts without nominal declarations. JavaScript's dynamic dispatch allows duck-typed doubles at runtime, while TypeScript can check the same doubles structurally before tests run. The organizing chain is therefore not only <em>principle → technique</em>, but <strong>design responsibility → technique → enabling language structure</strong>.</p>
<h2>Map responsibilities, not syntax</h2>
<p>Dependency Inversion and dependency injection are related, but they are not the same thing:</p>
<ul>
<li><strong>Dependency Inversion</strong> (the D in SOLID) is a principle about <strong>direction</strong>: high-level policy must not depend on low-level detail; both depend on abstractions.</li>
<li><strong>Dependency injection</strong> is a technique that serves it: bind a concrete dependency outside the code that consumes it.</li>
</ul>
<p>The OOP version stores an injected dependency in an object:</p>
<pre><code class="language-typescript">class RegistrationService {
  constructor(private readonly users: UserRepository) {}

  async register(input: RegisterInput) {
    const existing = await this.users.findByDocument(input.document);
    // ...
  }
}</code></pre>
<p>The functional version stores the same dependency in a closure:</p>
<pre><code class="language-typescript">function makeRegistrationService(users: UserRepository) {
  return {
    async register(input: RegisterInput) {
      const existing = await users.findByDocument(input.document);
      // ...
    },
  };
}</code></pre>
<p>Both consumers depend on <code>UserRepository</code>, not on a database implementation. Both receive the concrete dependency from somewhere else. The difference is where the language stores the provided value: an object field in one case, a lexical environment in the other.</p>
<p>Composition completes the mapping:</p>
<pre><code class="language-typescript">// composition/application.ts
import { postgresUserRepository } from &#039;../infrastructure/repository/postgres.user.repository&#039;;

function createRegistrationApplication(users: UserRepository) {
  const registration = makeRegistrationService(users);
  return { register: registration.register };
}

const application = createRegistrationApplication(postgresUserRepository);

await application.register(input);</code></pre>
<p>The composition root chooses the Postgres implementation, injects it once, and receives an application facade. Calling <code>application.register</code> does not choose, construct, or locate a repository. In a container-based application, a Service Container may perform this composition at the root. Here the composition function performs it manually. Neither the composition function nor the returned facade is itself a Service Container.</p>
<p>This is also where Interface Segregation fits. <code>UserRepository</code> should describe only what the registration Use Case needs. Structural typing does not guarantee ISP (nothing prevents us from declaring a fat contract), but it removes much of the implementation cost of keeping contracts small. Any value with the required shape can satisfy the consumer's contract without joining an inheritance hierarchy or declaring <code>implements</code>. (More details about <code>implements</code> down in the article.)</p>
<h2>Service factories, closures, and composition</h2>
<p>Three elements do different work here: the <strong>service factory creates</strong> a service, its parameters are the <strong>injection boundary</strong>, and the returned functions' <strong>closures retain</strong> the injected dependencies.</p>
<p>At the service level, calling <code>makeRegistrationService(users)</code> supplies the contract-typed dependency and creates the service. Every returned function then closes over <code>users</code>. This is the direct functional counterpart of a constructor receiving a value, assigning it to a private field, and producing an object instance, without exposing the dependency to every service call.</p>
<p>At the application level, a composition function can invoke several service factories and build the graph. The next example generalises the earlier registration-only case into a composition function for the full application graph:</p>
<pre><code class="language-typescript">interface ApplicationDependencies {
  users: UserRepository;
  mailer: Mailer;
  clock: Clock;
}

function createApplication(dependencies: ApplicationDependencies) {
  const registration = makeRegistrationService(dependencies.users);
  const profile = makeProfileService(dependencies.users);
  const reminders = makeReminderService(dependencies.mailer, dependencies.clock);

  return {
    register: registration.register,
    getProfile: profile.get,
    sendReminders: reminders.send,
  };
}</code></pre>
<p>Call <code>createApplication(...)</code> once at the composition root, and each returned service closes over only the slice of the graph it needs; collectively, those services expose the complete, already-wired application. Production passes database, mail, and system-clock implementations. Tests pass in-memory repositories, recording mailers, and fixed clocks. The consumers remain unchanged.</p>
<p>Closure-based composition can make a Service Container unnecessary for a simple, static graph, but it is not a full replacement for the abstraction. The composition function builds the graph and each returned closure retains its relevant part. Together they cover the container's core composition responsibility: construct the graph centrally, bind implementations to contracts, and expose ready-to-use services. A container may additionally provide autowiring, named bindings, scopes, lifecycle management, decorators, interception, or plugin discovery, and it remains available in functional code when those capabilities earn their cost. In TypeScript, autowiring still needs runtime anchors such as classes or explicit string/symbol tokens; erased structural interfaces cannot be resolved directly. Manual composition is sufficient when those capabilities are not needed.</p>
<p>The two routes can now be summarised without treating module syntax as part of the comparison:</p>
<ul>
<li><strong>OOP:</strong> segregated interface → constructor/factory → dependencies retained in object fields → manual or container-assisted composition → root application object</li>
<li><strong>Functional TypeScript:</strong> structural contract → service factory → dependencies retained in closures → manual or container-assisted composition → application facade</li>
</ul>
<p>The architecture is preserved because each responsibility still has an explicit home. What changes is the default mechanism: lexical scope replaces instance fields, service factories replace construction through <code>new</code>, structural conformance replaces nominal declarations, and explicit composition can replace container resolution when the graph does not need richer container capabilities.</p>
<p>Anton van Straaten's fictional Qc Na koan captures the first equivalence: <em>&quot;objects are a poor man's closures; closures are a poor man's objects.&quot;</em> Both bundle behaviour with retained state. Here that state includes the in-memory repository's <code>Map</code> and, in each service closure, precisely the dependencies that service needs.</p>
<h2>Structural typing: the conformance mechanism</h2>
<p>Service factories and closures answer <em>how one service is created and retains its injected dependencies</em>; the composition root answers <em>where the full graph is assembled</em>. Structural typing answers a simpler question: how does the compiler know a value satisfies the contract its consumer expects? It's what makes the functional mapping safe, not just short.</p>
<p>In a structurally typed system, a value satisfies a type by having the right <strong>shape</strong>: the required members, with compatible types. Names and declarations are irrelevant. <code>UserRepository</code> defines one such shape:</p>
<pre><code class="language-typescript">interface UserRepository {
  findByDocument(document: string): Promise&lt;User | null&gt;;
  create(data: NewUser): Promise&lt;User&gt;;
}</code></pre>
<p>Any value with those two methods, and compatible signatures, has that shape. So when the in-memory repository assigns its object literal to that type. Consider:</p>
<pre><code class="language-typescript">export const inMemoryUserRepository: UserRepository = { findByDocument, create };</code></pre>
<p>That single line <strong>is the conformance check.</strong> It's compile-time, exhaustive, and free: if the object is missing a method, has one with an incompatible signature, or the contract later grows, this line stops compiling. No <code>implements</code>, no registration, no adapter class. And it generalises: any object literal, class instance, or factory result that has the shape <em>is</em> a <code>UserRepository</code>. The contract is decoupled from every implementation's ancestry, which is why an in-memory object literal and a Postgres-backed implementation can be substituted at the composition root without changing a consumer.</p>
<p>You can watch the check evaporate the moment it's done its job. The in-memory repository didn't need <code>implements</code>. A Postgres-backed class can still use it for clarity, but it's optional, and it disappears at runtime. The TypeScript source:</p>
<pre><code class="language-typescript">class PostgresUserRepository implements UserRepository {
  async findByDocument(document: string) { /* ... */ }
  async create(data: NewUser) { /* ... */ }
}</code></pre>
<p>compiles to plain JavaScript with no trace of the contract:</p>
<pre><code class="language-javascript">class PostgresUserRepository {
  async findByDocument(document) { /* ... */ }
  async create(data) { /* ... */ }
}</code></pre>
<p>The <code>interface</code> and <code>implements</code> annotation disappear because they are type-system constructs. The class remains because a class is also a runtime JavaScript construct. Conformance was proven once, before the program ran, and then discarded. It's also why a TypeScript DI container can't autowire by interface the way C# or PHP can: at runtime there's no <code>UserRepository</code> to resolve against, only classes and string/symbol tokens. The same erasure that keeps contracts lightweight is the reason reflection-based containers cannot autowire by interface and must use runtime classes or explicit tokens.</p>
<p>This is also where the <strong>I</strong> of SOLID becomes cheaper to practise. A small contract requires little implementation ceremony (no base class, no <code>implements</code>, no registration), so keeping interfaces role-specific adds less friction. In a nominal OOP language, each new segregated interface adds a declaration that its implementations must adopt; structural typing removes that declaration tax. It does not guarantee Interface Segregation, but it makes the principle easier to apply consistently.</p>
<p>The contrast that makes this concrete is a nominal language, PHP, the reference OOP case here:</p>
<pre><code class="language-php">final class PostgresUserRepository implements UserRepository { /* ... */ }

function register(UserRepository $users): void { /* ... */ }</code></pre>
<p>Here conformance is <em>declared</em>. An object with exactly the right methods but no <code>implements UserRepository</code> is <strong>not</strong> a <code>UserRepository</code>: pass it to <code>register()</code> and PHP throws a <code>TypeError</code>. Same shape, wrong ancestry, rejected. This is one reason adapters and containers become more prominent in nominal ecosystems: conformance is explicitly declared, so wiring becomes a first-class activity with its own tooling.</p>
<p>One distinction worth stating now, because it drives the rest of the post: TypeScript is structural for interfaces, object types, and (mostly) classes, and it checks that conformance <strong>statically</strong>, before the program runs. &quot;Checked statically&quot; is easy to wave past, but it's the entire difference between structural typing and the thing it gets mistaken for. And the fastest place to feel that difference isn't in production at all. It's the moment you sit down to write a test.</p>
<p>And I am a Test-Driven Developer. Therefore...</p>
<h2>Testing: where duck typing walks back in</h2>
<p>The style survives contact with production easily enough. The seam is typed, the closure captures a contract-typed value. Where it gets interesting is the first time you write a test.</p>
<p>To test a service in isolation, you need a stand-in for its dependency, a repository that never touches a database. In JS/TS we've done this for years with an ad-hoc object: just the methods the test exercises, nothing more. And here the old instinct kicks in, reach for a loose double:</p>
<pre><code class="language-typescript">// duck-typed double: no contract in sight
const users = { findByDocument: async () =&gt; null } as any;</code></pre>
<p>This works. The test runs, the service calls <code>findByDocument</code>, the call resolves. That's <strong>duck typing</strong>: the object is accepted because, at the moment of the call, it happens to have the method. Nothing checked its shape ahead of time; the <code>as any</code> explicitly threw the check away.</p>
<p>And it raises the question this whole post circles: <strong>is that the same thing as structural typing?</strong> It feels identical, both say &quot;shape is enough, ancestry is irrelevant.&quot; But the difference is exactly the <em>when</em>. Type the same double against the contract instead:</p>
<pre><code class="language-typescript">// structurally-typed double: checked against the contract, now
const users: UserRepository = {
  findByDocument: async () =&gt; null,
  create: async (data) =&gt; ({ id: &#039;x&#039;, ...data, createdAt: new Date() }),
};</code></pre>
<p>Now the compiler enforces the whole shape <em>before the test runs</em>. Forget <code>create</code>, or let the real <code>UserRepository</code> grow a method, and this double stops compiling; the test tells you it drifted. The <code>as any</code> version compiles happily and only fails later, at runtime, if that path is even exercised.</p>
<p>Same philosophy, opposite failure mode. The loose double (<code>as any</code>) is widely used and genuinely convenient, and it's quietly the reason so many of us assume duck typing and structural typing are one thing. They aren't: typing the double <code>: UserRepository</code> is the small discipline that keeps a test double in the structural camp, and it's worth doing. But the recommendation isn't the point here. The point is that the moment you write a mock is the moment the two concepts visibly separate, which is exactly what the next section pulls apart.</p>
<h2>Untangling the five concepts</h2>
<p>Five ideas have been doing distinct work so far. Placing them side by side makes the boundaries explicit:</p>
<table>
<thead>
<tr>
<th>Concept</th>
<th>What it is</th>
<th>What it is not</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Dependency inversion / injection</strong></td>
<td>DIP: a principle about <em>direction</em>; depend on abstractions (the D in SOLID). DI: a <em>technique</em> serving it; bind concretions elsewhere</td>
<td>A framework, a container, or anything inherently class-shaped</td>
</tr>
<tr>
<td><strong>OOP constructs</strong></td>
<td>Classes, constructors, <code>implements</code>; <em>one mechanism</em> for DI and encapsulation</td>
<td>The definition of DI, or a requirement for Interface Segregation or Dependency Inversion</td>
</tr>
<tr>
<td><strong>Closures</strong></td>
<td>Functions capturing their environment; <em>another mechanism</em> for retained private state, injected dependencies, and explicit graph composition</td>
<td>A Service Container abstraction by definition, or a hack until &quot;real&quot; OOP arrives</td>
</tr>
<tr>
<td><strong>Structural typing</strong></td>
<td><strong>Static</strong> conformance decided by shape; checked before the program runs</td>
<td>A dynamic-language feature; permission to skip contracts</td>
</tr>
<tr>
<td><strong>Duck typing</strong></td>
<td><strong>Runtime</strong> conformance: &quot;if it quacks&quot;; discovered at the call site, or not at all</td>
<td>A synonym for structural typing</td>
</tr>
</tbody>
</table>
<p>Read down the &quot;what it is not&quot; column and the responsibilities separate cleanly. <strong>Dependency injection is not a framework</strong>: an explicit composition function can bind the graph as truly as a container. <strong>OOP constructs are not the definition of DI</strong>: they're one mechanism among several, the one that happens to dominate nominal ecosystems. <strong>Closures are not a hack or a container by definition</strong>: they are a language mechanism that can retain one dependency or the complete composed graph.</p>
<p>The two that cause the most trouble are the last ones: <strong>structural typing vs duck typing.</strong> They share a philosophy, behaviour over ancestry, shape over name, and in TypeScript they even look identical to write, which is why they get treated as one thing. They are not. The difference is <em>when the shape is checked</em>. Structural typing checks it at <strong>compile time</strong>, by a type checker, before the program runs; the failure is a red squiggle in your editor. Duck typing checks it at <strong>runtime</strong>, at the moment of the call, by the call itself; the failure is a thrown error, or worse, silence, if the missing method simply never gets reached on that path.</p>
<p>Which answers the question the testing section left open: if structural typing isn't what <em>makes duck typing possible</em>, what does? The enabler is <strong>dynamic dispatch</strong>, the language resolving <code>x.quack()</code> against whatever <code>x</code> actually is at call time, with no prior type commitment required. That's a property of the runtime, not of the type system, and it's independent of whether conformance is judged by shape or by declaration. The clean proof is Python: it has had duck typing forever, with no structural type system at all; <code>typing.Protocol</code> (PEP 544) later <em>added</em> static structural checking as an opt-in for external type checkers, and the duck-typed runtime underneath didn't change one bit. So duck typing and structural typing aren't two names for one idea, they're independent answers to two different questions, <em>how?</em> and <em>when?</em>, and a language can choose each answer separately.</p>
<h2>The language field guide</h2>
<p>Put the two questions on two axes, <strong>how</strong> conformance is decided (by shape, or by declaration) and <strong>when</strong> it's checked (statically at compile time, or at runtime), and every language lands in a cell:</p>
<table>
<thead>
<tr>
<th></th>
<th>Checked statically (compile time)</th>
<th>Checked at runtime</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>By shape</strong></td>
<td>TypeScript, Go (<em>structural typing</em>)</td>
<td>Python, JS, Ruby (<em>duck typing</em>)</td>
</tr>
<tr>
<td><strong>By declaration</strong></td>
<td>C#, Java (<em>nominal + static</em>)</td>
<td>PHP with type hints (<em>nominal + runtime</em>)</td>
</tr>
</tbody>
</table>
<p>The cells people forget are the interesting ones.</p>
<ul>
<li><strong>TypeScript</strong>: shape, static, and <em>loose</em>. An ad-hoc object literal conforms on the spot, no named type required. The cheapest contracts of any mainstream language. TypeScript provides static structural checking over a dynamically typed JavaScript runtime, so once the types are erased, what's left is duck typing.</li>
<li><strong>Go</strong>: shape, static, and <em>not loose</em>. Interfaces are satisfied implicitly (structural, a type never says <code>implements</code>), but conformance still needs methods declared on a named type; there are no ad-hoc conforming literals and no runtime &quot;try the call and see.&quot; Interface satisfaction operates through named interface types rather than TypeScript-style ad-hoc object compatibility. This is the answer to <strong>&quot;is there structural typing without any duck typing?&quot;</strong>. Go is it, and it isn't a compromise. The idiomatic Go move of defining tiny consumer-side interfaces is Interface Segregation and structural typing working together, straight out of the textbook.</li>
<li><strong>Python / JS / Ruby</strong>: shape, runtime. Classic duck typing, enabled by dynamic dispatch. Python then bolts on optional static structural typing via <code>typing.Protocol</code> (PEP 544) for anyone running a type checker, without touching the duck-typed runtime, the cleanest demonstration that the two are separable.</li>
<li><strong>PHP</strong>: declaration, runtime. Nominal, but enforced at call time. With type declarations, objects of classes that declare <code>implements</code> pass; a mismatch is a <code>TypeError</code> when the call happens, not a compile error (PHPStan/Psalm drag the check earlier, as a lint). Drop the declarations and PHP becomes dynamically typed: no type enforcement on parameters, method calls resolved at runtime, but objects are still class instances, not ad-hoc shapes. That double identity, nominal-when-typed, dynamic-when-not, plus autowiring by type declaration is the same story that made the containers from section 3 inevitable.</li>
<li><strong>C# / Java</strong>: declaration, static. Nominal, checked at compile time. The full ceremony (declare the interface, <code>implements</code> it, register it, inject it) and perfectly capable of clean dependency-inverted designs; the container exists to amortise the wiring cost the nominal type system creates in the first place.</li>
</ul>
<p>The grid shows these are <strong>independent axes</strong>, and real languages occupy all four cells. Go is structural and statically checked, but without TypeScript's ad-hoc object compatibility; TypeScript is structural-and-loose yet fully static; Python is duck-typed with an optional structural upgrade; PHP is nominal without being static, the quadrant people forget exists. &quot;Structural&quot; and &quot;duck&quot; are not synonyms, and &quot;nominal&quot; and &quot;static&quot; are not the same axis. Once the grid is visible, the testing question becomes precise: a TypeScript double can rely on JavaScript's runtime duck typing, or it can be checked structurally before the test runs. The object may look the same; the guarantee is different.</p>
<h2>A good way to use them</h2>
<p>None of this argues against classes, and none of it argues against containers. It argues for choosing the mechanism on purpose. A few defaults fall out of everything above:</p>
<ul>
<li><strong>Keep contracts in the domain, named in the domain's language.</strong> <code>UserRepository</code> belongs in <code>domain/user.ts</code>, not in the infrastructure that implements it; the consumer owns the interface, the implementation conforms to it. This is the Go consumer-defined-interface instinct and Interface Segregation in one move.</li>
<li>For simple functional services, a factory + closure is a natural default. The dependency is captured once and stays private. Keep <strong>parameter passing</strong> for genuinely local composition or deliberate scaffolding. Put the complete graph in an explicit <strong>composition root</strong> (<code>createApplication</code> or equivalent), invoke it once with production implementations, and retain the returned application facade as the wired root object. That is where implementation choices remain centralised and tests gain a single, typed substitution boundary.</li>
<li><strong>Treat class-vs-closure as a mechanism choice, not a moral one.</strong> Pick by team convention and by whether you genuinely need identity or lifecycle semantics, not because one of them is &quot;real&quot; dependency injection. They both are.</li>
</ul>
<p>It's worth naming the design principle underneath, because it's the reason the cheap option is also the good one. In Vlad Khononov's Balanced Coupling model, a shared contract is the <strong>weakest, most distance-tolerant</strong> form of coupling between two components, exactly what you want across a module boundary, where the two sides may change and ship independently. Structural typing makes that weakest level of coupling nearly free to express, so in TypeScript the best-balanced design and the least-ceremony design are the same design. The one discipline the model insists on: keep the wiring <strong>explicit and greppable</strong>, a named seam you can find, because Balanced Coupling's warning is against <em>implicit</em> coupling, and implicit coupling is also structural typing's failure mode. Which is the honest counterpoint.</p>
<h2>The honest counterpoint</h2>
<p>The price of making conformance free is that conformance becomes <em>implicit</em>. Anything with the right shape satisfies a contract, including things that match only by accident. A single-method contract like <code>{ execute(): void }</code> is satisfied by half the objects in a codebase; the type checker will happily accept a wrong one that merely fits. The narrower and more generic the contract, the more coincidental matches it invites, so the defence is to keep contracts <strong>role-named and non-trivial</strong> (<code>UserRepository</code>, not <code>Doer</code>), so that matching the shape actually means matching the intent.</p>
<p>Implicitness costs tooling, too. Without <code>implements</code>, &quot;find all implementations of this interface&quot; is a weaker query than in a nominal language, and renaming a contract member doesn't announce, at the declaration site, everyone who just broke. You lean harder on the compiler and on find-references, and a little less on the class hierarchy telling you who's involved.</p>
<p>The mitigation turns out to be the same thing as the recommendation from the previous section: <strong>an explicit, greppable composition root.</strong> A deliberate conformance point, <code>const impl: Contract = ...</code>, typed against the contract, living in that known location, turns implicit shape-matching back into a visible, findable decision. It's also, not coincidentally, the same discipline that keeps a <em>test double</em> structural rather than duck-typed. The functional style doesn't remove the need to be deliberate about coupling; it relocates the deliberateness from the type declaration to the wiring, and rewards you for keeping that wiring in the open.</p>
<h2>Checklist</h2>
<p>A quick test for &quot;am I doing DI properly in this style?&quot;, whether or not you reached for a class:</p>
<ul>
<li>Does the consumer depend on a <strong>contract</strong>, not a concrete implementation? (Take the contract as a factory parameter and capture it.)</li>
<li>Is the <strong>binding external</strong> to the consumer, captured by a service factory and selected at the composition root, rather than constructed inline?</li>
<li>Is every implementation, <strong>including test doubles</strong>, typed against the contract (<code>: UserRepository</code>), so drift fails at compile time instead of at runtime?</li>
<li>Are contracts <strong>small and role-named</strong>, so structural matching signals intent rather than coincidence?</li>
<li>Is the complete dependency graph <strong>explicit and greppable</strong> in one composition root, rather than having consumers select concrete implementations throughout the application?</li>
<li>Did you pick class-vs-closure for a <strong>real reason</strong> (convention, lifecycle, identity), not out of habit or a belief that one is &quot;real&quot; DI?</li>
</ul>
<hr />
<h2>Recap</h2>
<p>The article started with a refactor that removed a temporary parameter and raised a question: was I giving up Interface Segregation and Dependency Inversion by moving from OOP to functional TypeScript, or was I looking for OOP mechanisms where functional equivalents existed?</p>
<p>The answer turned out to be a comparison worth making. For every responsibility that had an obvious home in OOP, there was a functional technique that gave it an equivalent home:</p>
<ul>
<li><strong>Interface Segregation</strong> lives in small, role-named contracts. Structural typing removes the declaration tax that keeps contracts fat in nominal ecosystems.</li>
<li><strong>Dependency Inversion</strong> lives in depending on abstractions, not on concretions. A factory parameter captures a contract-typed dependency as cleanly as a constructor.</li>
<li><strong>Dependency Injection</strong> lives in binding concretions outside the consumer. A service factory and closure retain injected state as truly as an object field.</li>
<li><strong>Composition</strong> lives in a single root where implementations are selected and the graph is assembled. A composition function does this as truly as a Service Container.</li>
<li><strong>Conformance</strong> lives in shape, not ancestry. Structural typing checks it statically, before the program runs. Duck typing discovers it at runtime, at the call site. Same philosophy, different <em>when</em>.</li>
<li>The language field guide put that distinction on two independent axes:
<ul>
<li><strong>How</strong> conformance is decided: by shape or by declaration.</li>
<li><strong>When</strong> it's checked: statically at compile time, or at runtime.</li>
</ul>
</li>
<li>Real languages occupy all four cells:
<ul>
<li><strong>Go</strong> is structural and statically checked, but without ad-hoc object compatibility.</li>
<li><strong>TypeScript</strong> is structural and loose, yet fully static.</li>
<li><strong>Python</strong> is duck-typed with an optional structural upgrade.</li>
<li><strong>PHP</strong> is nominal without being static.</li>
</ul>
</li>
<li>&quot;Structural&quot; and &quot;duck&quot; are not synonyms, and &quot;nominal&quot; and &quot;static&quot; are not the same axis.</li>
<li>The honest counterpoint: making conformance free makes it implicit. Anything with the right shape satisfies a contract, including things that match only by accident. The defence is role-named, non-trivial contracts and an explicit, greppable composition root. A deliberate conformance point, typed against the contract, turns implicit shape-matching back into a visible, findable decision.</li>
<li>The principles stayed the same. The mechanisms changed:
<ul>
<li><strong>Classes, constructors, and</strong> <code>implements</code> are one set of mechanisms.</li>
<li><strong>Closures, factories, and structural contracts</strong> are another.</li>
</ul>
</li>
<li>Neither set is the definition of the principles they carry. Picking between them is a mechanism choice, not a moral one.</li>
<li>The one discipline that travels across both styles: <strong>keep the wiring explicit and greppable</strong>. A named seam you can find is the difference between deliberate coupling and accidental coupling, whether the seam is a constructor parameter or a factory argument.</li>
</ul>
<hr />
<h2>Glossary</h2>
<ul>
<li><strong>OOP — Object-Oriented Programming:</strong> a programming style that organises state and behaviour around objects. For comparison, this article uses a common class-based arrangement: interfaces, constructors, implementations, and a Service Container.</li>
<li><strong>SOLID:</strong> a family of five design principles associated with maintainable and adaptable software. This article focuses only on its <strong>I</strong> and <strong>D</strong>.</li>
<li><strong>ISP — Interface Segregation Principle:</strong> consumers should not be forced to depend on operations they do not use; prefer small, role-specific contracts.</li>
<li><strong>DIP — Dependency Inversion Principle:</strong> high-level policy should not depend directly on low-level details; both should depend on abstractions, and details should depend on those abstractions.</li>
<li><strong>DI — Dependency Injection:</strong> a technique for supplying dependencies from outside the consumer. DI can support DIP, but the two are not synonyms.</li>
<li><strong>Closure:</strong> a function together with the lexical environment it retains, allowing injected values to remain available after the factory that received them has returned.</li>
<li><strong>Service Container:</strong> a central assembler or registry that selects implementations and wires services, often with additional lifecycle or resolution features.</li>
<li><strong>Composition root:</strong> the single application location where concrete implementations are selected and the dependency graph is assembled.</li>
<li><strong>Seam:</strong> a place in code where behaviour can be varied without editing the code that uses it. Coined by Michael Feathers in <em>Working Effectively with Legacy Code</em>. In this article, the repository parameter is a seam: you swap implementations at the call site without touching the service.</li>
</ul>
<h2>Further reading and references</h2>
<h3>Glossary concepts</h3>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Glossary/OOP">OOP - MDN glossary</a> — concise introduction to object-oriented programming and JavaScript's prototype-based model.</li>
<li><a href="https://en.wikipedia.org/wiki/SOLID">SOLID - Wikipedia</a> — the five-principle family; this article focuses on ISP and DIP.</li>
<li><a href="https://objectmentor.com/resources/articles/isp.pdf">The Interface Segregation Principle - Robert C. Martin</a> (PDF) — the original detailed treatment of cohesive, client-specific interfaces.</li>
<li><a href="https://objectmentor.com/resources/articles/dip.pdf">The Dependency Inversion Principle - Robert C. Martin</a> (PDF) — the original statement that policy and detail should depend on abstractions.</li>
<li><a href="https://www.martinfowler.com/articles/injection.html">Inversion of Control Containers and the Dependency Injection Pattern — Martin Fowler</a> — dependency injection, container wiring, and the separation of configuration from use.</li>
<li><a href="https://blog.ploeh.dk/2011/07/28/CompositionRoot/">Composition Root - Mark Seemann</a> — the application location where modules and concrete dependencies are assembled.</li>
<li><a href="https://www.informit.com/store/working-effectively-with-legacy-code-9780131177055">Working Effectively with Legacy Code - Michael Feathers</a> — the origin of the &quot;seam&quot; concept: a place in code where behaviour can be varied without editing the code that uses it.</li>
</ul>
<h3>Language and design mechanics</h3>
<ul>
<li><a href="https://www.typescriptlang.org/docs/handbook/type-compatibility">TypeScript Handbook - Type Compatibility</a> — TypeScript's structural type system.</li>
<li><a href="https://go.dev/ref/spec">The Go Programming Language Specification - Interface types</a> — implicit, statically checked interface satisfaction.</li>
<li><a href="https://peps.python.org/pep-0544/">PEP 544 - Protocols: Structural Subtyping</a> — Python's opt-in static structural typing.</li>
<li><a href="https://www.php.net/manual/en/language.types.type-system.php">PHP Manual - Type System</a> — PHP's nominal type system and runtime verification.</li>
<li><a href="https://coupling.dev/">Balanced Coupling - Vlad Khononov</a> — integration strength, distance, volatility, and contract coupling.</li>
<li><a href="https://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html">The Qc Na closures/objects koan - Anton van Straaten</a> — the equivalence between retained state in closures and objects.</li>
<li><a href="https://www.infoq.com/presentations/Simple-Made-Easy/">Simple Made Easy - Rich Hickey</a> — separating simplicity from familiarity and convenience.</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://rafael.bernard-araujo.com/the-principle-is-not-the-mechanism-isp-dip-and-language-structures-in-practice.php/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2420</post-id>	</item>
		<item>
		<title>Tropeçando 120</title>
		<link>https://rafael.bernard-araujo.com/tropecando-120.php</link>
					<comments>https://rafael.bernard-araujo.com/tropecando-120.php#respond</comments>
		
		<dc:creator><![CDATA[rafael]]></dc:creator>
		<pubDate>Mon, 18 May 2026 00:02:13 +0000</pubDate>
				<category><![CDATA[Tropeçando]]></category>
		<category><![CDATA[ai]]></category>
		<category><![CDATA[container]]></category>
		<category><![CDATA[developer experience]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[machine learning]]></category>
		<category><![CDATA[software architecture]]></category>
		<category><![CDATA[software engineering]]></category>
		<guid isPermaLink="false">https://rafael.bernard-araujo.com/?p=2363</guid>

					<description><![CDATA[AI Management &#38; Organizational Restructuring The Foreman Problem: Managing Teams When Your Best Worker Isn't Human - Willian Correa Every major technology shift invented a new management role. Steam power → foreman. Office computing → project manager. Internet → product manager. AI is doing the same, but this time the failure mode is invisible: confident, [&#8230;]]]></description>
										<content:encoded><![CDATA[<h3>AI Management &amp; Organizational Restructuring</h3>
<p><a href="https://businessasusual.io/p/the-foreman-problem-managing-teams">The Foreman Problem: Managing Teams When Your Best Worker Isn't Human</a> - Willian Correa</p>
<blockquote>
<p>Every major technology shift invented a new management role. Steam power → foreman. Office computing → project manager. Internet → product manager. AI is doing the same, but this time the failure mode is invisible: confident, polished, wrong output. The new job is not directing effort but verifying that things that <em>look</em> like they're running actually are.</p>
</blockquote>
<p><a href="https://theengineeringmanager.substack.com/p/who-will-be-the-senior-engineers">Who Will Be the Senior Engineers of 2035?</a> - James Stanier</p>
<blockquote>
<p>The traditional junior-to-senior pipeline is breaking: entry-level tech postings down 67% since 2022, junior employment down ~20%. Firms adopting AI saw junior employment fall 7.7% vs non-adopters. 54% of engineering leaders plan to hire fewer juniors.</p>
</blockquote>
<h3>Compound Engineering &amp; Code Health</h3>
<p><a href="https://refactoring.fm/p/the-compounding-software-factory">The Compounding Software Factory</a> - Luca Rossi (Software Factory series, Part 3 of 3)</p>
<blockquote>
<p>What causes teams to degrade: poor coding hygiene (bad testing, poor code health, missing abstractions), failure to capture knowledge (no ADRs, no playbooks, no snapshots), and building the wrong things.</p>
</blockquote>
<p><a href="https://refactoring.fm/p/ai-coding-meets-code-health-with">AI Coding Meets Code Health</a> - Stuart Caborn</p>
<blockquote>
<p>Loveholidays' journey to becoming an AI-first engineering organization. Core thesis: code health is the foundation for successful AI adoption. By deliberately investing in code health metrics <em>before</em> adopting AI, they achieved 80+ deployments/month, 60% AI-written code, &lt;1% change failure rate, all while maintaining elite code health.</p>
</blockquote>
<h3>Security &amp; Infrastructure</h3>
<p><a href="https://www.wiz.io/blog/github-actions-security-ai-powered-actions-vulnerabilities">The (In)security Landscape of AI-Powered GitHub Actions</a> - Shay Berkovich</p>
<blockquote>
<p>AI-powered GitHub Actions from vendors like OpenAI, Anthropic, and Google are now running in thousands of public workflows. Research found bypasses of non-default configurations letting any external attacker trigger AI execution, a novel secret exfiltration vector for dynamically-created credential files, and widespread misconfigurations in production workflows.</p>
</blockquote>
<p><a href="https://www.allthingsdistributed.com/2026/04/the-invisible-engineering-behind-lambdas-network.html">The Invisible Engineering Behind Lambda's Network</a> - Werner Vogels</p>
<blockquote>
<p>A decade-long story of invisible infrastructure engineering by Lambda's networking team.</p>
</blockquote>
<h3>Career &amp; Token Economics</h3>
<p><a href="https://businessasual.io/p/tokenmaxxing-is-the-budget-game-played">Tokenmaxxing Is the Budget Game Played With AI Tokens</a> - Willian Correa</p>
<blockquote>
<p>Tokenmaxxing — maximising AI token consumption for visibility — is the corporate &quot;use it or lose it&quot; budget game in a new currency. Meta's internal &quot;Claudeonomics&quot; leaderboard ranked 85K employees by token consumption; top user burned 281B tokens in 30 days.</p>
</blockquote>
<h3>Tools</h3>
<p><a href="https://docs.docker.com/compose/how-tos/file-watch/">Use Compose Watch</a></p>
<p>Docker bind volumes gets a supercharge. Compose Watch does not replace bind mounts but exists as a companion specifically suited to developing in containers.</p>
<blockquote>
<p>More importantly, watch allows for greater granularity than is practical with a bind mount. Watch rules let you ignore specific files or entire directories within the watched tree.<br />
For example, in a Node.js project, it's not recommended to sync the node_modules/ directory. Even though JavaScript is interpreted, npm packages can contain native code that is not portable across platforms.</p>
</blockquote>
]]></content:encoded>
					
					<wfw:commentRss>https://rafael.bernard-araujo.com/tropecando-120.php/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2363</post-id>	</item>
		<item>
		<title>Tropeçando 119</title>
		<link>https://rafael.bernard-araujo.com/tropecando-119.php</link>
					<comments>https://rafael.bernard-araujo.com/tropecando-119.php#respond</comments>
		
		<dc:creator><![CDATA[rafael]]></dc:creator>
		<pubDate>Tue, 21 Apr 2026 04:18:20 +0000</pubDate>
				<category><![CDATA[Tropeçando]]></category>
		<category><![CDATA[agent tools]]></category>
		<category><![CDATA[process management]]></category>
		<category><![CDATA[secrets management]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[serverless]]></category>
		<category><![CDATA[software engineering]]></category>
		<guid isPermaLink="false">https://rafael.bernard-araujo.com/?p=2350</guid>

					<description><![CDATA[How to Grow your Software Factory Luca Rossi argues that the right measure of AI effectiveness isn't lines of code but leverage — how much output you get per unit of human input. Teams progress through three stages: writing full specs for everything, then encoding knowledge into shared rules (like AGENTS.md), and finally building reusable [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><a href="https://refactoring.fm/p/growing-your-sofware-factory">How to Grow your Software Factory</a> </p>
<p>Luca Rossi argues that the right measure of AI effectiveness isn't lines of code but leverage — how much output you get per unit of human input. Teams progress through three stages: writing full specs for everything, then encoding knowledge into shared rules (like AGENTS.md), and finally building reusable modules that enforce correctness by design.</p>
<p><a href="https://newsletter.theburningmonk.com/posts/the-security-case-for-serverless-just-got-stronger">The security case for serverless just got stronger</a></p>
<blockquote>
<p>AI agents can now scan an entire open-source codebase for exploitable vulnerabilities in hours.</p>
<p>Frontier models carry the complete library of known bug classes in their weights. So you can simply point an AI agent at a codebase and tell it to find zero-days.</p>
<p>This isn't theoretical.</p>
</blockquote>
<p>Yan Cui highlights that AI agents can now find real zero-days in open-source codebases at scale, shrinking the patch window from weeks to hours. Serverless and managed services have a structural advantage because AWS patches the runtime for you. The practical takeaways: eliminate long-lived AWS keys everywhere, treat LLM API keys like credentials, and scan your repos for exposed secrets.</p>
<p><a href="https://www.nodejs-security.com/blog/do-not-use-secrets-in-environment-variables-and-here-is-how-to-do-it-better">Do not use secrets in environment variables and here's how to do it better</a></p>
<p><a href="https://apenwarr.ca/log/20260316">Every Layer of Review Makes You 10x Slower</a></p>
<p>Each approval layer adds 10x wall clock time, and AI can't fix that. It only speeds up the first step. Drawing on Deming and the Toyota Production System, the argument is that review layers hide root causes rather than fixing them. The memorable line: <em>&quot;The job of a code reviewer isn't to review code — it's to figure out how to obsolete their review comment, that whole class of comment, forever.&quot;</em></p>
<p>The common thread across all four: the bottleneck isn't writing code, it's the systems around it. Whether it's review layers, security patching, or AI leverage, the answer is the same: engineer quality into the system itself through tests, automation, modules, and clear interfaces, rather than adding layers of inspection after the fact.</p>
<p><a href="https://www.theregister.com/2026/04/13/claude_code_cache_confusion/">Claude Code cache chaos creates quota complaints</a></p>
<p>Anthropic changed the prompt cache TTL from 1 hour to 5 minutes in March. Long, high-context sessions hit quota limits much faster. Pro users report as few as 2 prompts per 5 hours. Leaving your machine for &gt;1 hour = full cache miss on the 1M token context. They're considering reducing the default to 400K tokens.</p>
<p>Token consumption matters more than ever. The next two tools address this from both ends.</p>
<p><a href="https://juliusbrussee.github.io/caveman/">Caveman — Output Token Compression</a></p>
<p>Constrains LLM output to minimal-token structures. Strips pleasantries and padding, keeps code and technical content. Up to 87% output token reduction. Paper shows brevity constraints improve accuracy by 26pp.</p>
<p><a href="https://github.com/rtk-ai/rtk">RTK (Rust Token Killer) — Input Token Compression</a></p>
<p>Intercepts shell command outputs (git, ls, grep, test runners, docker, AWS CLI — 100+ commands) and compresses them before they reach the LLM context. 60-90% input token reduction, &lt; 10ms overhead.</p>
<p>Works with: Claude Code, Copilot, Gemini CLI, Codex, Cursor, Windsurf, Cline.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://rafael.bernard-araujo.com/tropecando-119.php/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2350</post-id>	</item>
		<item>
		<title>Tropeçando 118</title>
		<link>https://rafael.bernard-araujo.com/tropecando-118.php</link>
					<comments>https://rafael.bernard-araujo.com/tropecando-118.php#respond</comments>
		
		<dc:creator><![CDATA[rafael]]></dc:creator>
		<pubDate>Mon, 09 Mar 2026 02:47:29 +0000</pubDate>
				<category><![CDATA[Tropeçando]]></category>
		<category><![CDATA[ddd]]></category>
		<category><![CDATA[domain-driven design]]></category>
		<category><![CDATA[ia]]></category>
		<category><![CDATA[middleware]]></category>
		<category><![CDATA[software architecture]]></category>
		<category><![CDATA[software engineering]]></category>
		<guid isPermaLink="false">https://rafael.bernard-araujo.com/?p=2273</guid>

					<description><![CDATA[Your AI Coding agent doesn’t know when to ask for help Why do multi-agent coding systems fall apart on complex, real-world tasks? How to Manage Context in AI Coding Focus on building multiplayer, dynamic systems that provide the right information reliably, rather than crafting magical wording. Design workflows where AI can fetch what it needs [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><a href="https://medium.com/@igorcosta/your-ai-coding-agent-doesnt-know-when-to-ask-for-help-75c10be496c5">Your AI Coding agent doesn’t know when to ask for help</a></p>
<blockquote>
<p>Why do multi-agent coding systems fall apart on complex, real-world tasks?</p>
</blockquote>
<p><a href="https://refactoring.fm/p/managing-context-for-ai-coding?utm_source=substack&amp;utm_campaign=post_embed&amp;utm_medium=web">How to Manage Context in AI Coding</a></p>
<blockquote>
<p>Focus on building multiplayer, dynamic systems that provide the right information reliably, rather than crafting magical wording. Design workflows where AI can fetch what it needs automatically.</p>
</blockquote>
<p><a href="https://martinfowler.com/bliki/ValueObject.html">Value Object</a></p>
<blockquote>
<p>When programming, I often find it's useful to represent things as a compound.</p>
</blockquote>
<p><a href="https://martinfowler.com/eaaDev/Range.html">Range - Further Enterprise Application Architecture development</a></p>
<blockquote>
<p>It's quite common to see comparisons where a value is checked against a range of values. Ranges are usually handled by a pair of values and you check against them both. Range instead uses a single object to represent the range as a whole, and then provides the relevant operations to test to see if values fall in the range and to compare ranges.</p>
</blockquote>
<p><a href="https://dzone.com/articles/jdk-memory-bloat-containers?email_hash=d5eba7663d4e04a1e0f4886103285fd8">JDK 17 Memory Bloat in Containers: A Post-Mortem</a></p>
<p>I just love runtime upgrades. Runtime upgrade are very important. And they need careful planning. Not unusual that they teach us important lessons for the next upgrade.</p>
<blockquote>
<p>When engineering teams modernize Java applications, the shift from JDK 8 to newer Long-Term Support (LTS) versions, such as JDK 11, 17, and soon 21, might seem straightforward at first. Since Java maintains backward compatibility, it's easy to assume that the runtime behavior will remain largely unchanged. However, that's far from reality.</p>
</blockquote>
<p><a href="https://tidyfirst.substack.com/p/my-fitbit-buzzed-and-i-understood?utm_source=post-email-title&amp;publication_id=256838&amp;post_id=180524868&amp;utm_campaign=email-post-title&amp;isFreemail=false&amp;r=6fpjy1&amp;triedRedirect=true&amp;utm_medium=email">My Fitbit Buzzed and I Understood Enshittification</a></p>
<blockquote>
<p>My Fitbit started buzzing at me a year ago. “It looks like you’re exercising.”</p>
<p>Product development is also an exercise in human relationships. And when we reduce those relationships to metrics, we lose something essential. We lose the ability to say, “This would be rude.” We lose the ability to treat users like people instead of engagement vectors.</p>
</blockquote>
<p><a href="https://maximegosselin.com/posts/using-the-middleware-pattern-to-extend-php-libraries/">Using the Middleware Pattern to Extend PHP Libraries</a></p>
<blockquote>
<p>PSR-15 did not invent middleware. But it showed the PHP community what a well-designed, typed middleware interface looks like. There is no reason to leave that idea at the HTTP layer.</p>
<p>If you maintain a PHP library with any non-trivial processing, consider building middleware support in from day one. Your users will thank you, and so will your future self.</p>
</blockquote>
]]></content:encoded>
					
					<wfw:commentRss>https://rafael.bernard-araujo.com/tropecando-118.php/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2273</post-id>	</item>
		<item>
		<title>Tropeçando 117</title>
		<link>https://rafael.bernard-araujo.com/tropecando-117.php</link>
					<comments>https://rafael.bernard-araujo.com/tropecando-117.php#respond</comments>
		
		<dc:creator><![CDATA[rafael]]></dc:creator>
		<pubDate>Thu, 20 Nov 2025 08:02:00 +0000</pubDate>
				<category><![CDATA[Tropeçando]]></category>
		<category><![CDATA[ai]]></category>
		<category><![CDATA[llm]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[site reliability]]></category>
		<category><![CDATA[software engineering]]></category>
		<guid isPermaLink="false">https://rafael.bernard-araujo.com/?p=2221</guid>

					<description><![CDATA[How far can we push AI autonomy in code generation? We ran a series of experiments to explore how far Generative AI can currently be pushed toward autonomously developing high-quality, up-to-date software without human intervention. As a test case, we created an agentic workflow to build a simple Spring Boot application end to end. We [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><a href="https://martinfowler.com/articles/pushing-ai-autonomy.html">How far can we push AI autonomy in code generation?</a></p>
<blockquote>
<p>We ran a series of experiments to explore how far Generative AI can currently be pushed toward autonomously developing high-quality, up-to-date software without human intervention. As a test case, we created an agentic workflow to build a simple Spring Boot application end to end. We found that the workflow could ultimately generate these simple applications, but still observed significant issues in the results—especially as we increased the complexity. The model would generate features we hadn't asked for, make shifting assumptions around gaps in the requirements, and declare success even when tests were failing. We concluded that while many of our strategies — such as reusable prompts or a reference application — are valuable for enhancing AI-assisted workflows, a human in the loop to supervise generation remains essential. </p>
</blockquote>
<p><a href="https://thephp.foundation/blog/2025/09/05/php-mcp-sdk/">Announcing the Official PHP SDK for MCP</a></p>
<blockquote>
<p>The PHP Foundation, Anthropic’s MCP team, and Symfony are collaborating on the official PHP SDK for the Model Context Protocol (MCP). Our goal is a framework-agnostic, production-ready reference implementation the PHP ecosystem can rely on.</p>
</blockquote>
<p><a href="https://ashallendesign.co.uk/blog/covariance-and-contravariance-in-php">Covariance and Contravariance in PHP </a></p>
<blockquote>
<p>Before we dive into the details and code examples, let me quickly define covariance and contravariance:</p>
<p>Covariance: Making something more specific<br />
Contravariance: Making something less specific</p>
<p>Now let's dive in and see how these concepts apply to PHP.</p>
</blockquote>
<p><a href="https://slack.engineering/break-stuff-on-purpose/">Break Stuff on Purpose</a></p>
<blockquote>
<p>Strengthen your system’s ability to recover by intentionally causing and resolving failures</p>
</blockquote>
<p><a href="https://read.thecoder.cafe/p/nothing-beats-kindness">Nothing Beats Kindness</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://rafael.bernard-araujo.com/tropecando-117.php/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2221</post-id>	</item>
		<item>
		<title>Principles in Refactoring &#8211; Slowing Down New Features?</title>
		<link>https://rafael.bernard-araujo.com/principles-in-refactoring-slowing-down-new-features.php</link>
					<comments>https://rafael.bernard-araujo.com/principles-in-refactoring-slowing-down-new-features.php#respond</comments>
		
		<dc:creator><![CDATA[rafael]]></dc:creator>
		<pubDate>Wed, 22 Oct 2025 02:59:39 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[software engineering]]></category>
		<guid isPermaLink="false">https://rafael.bernard-araujo.com/?p=2252</guid>

					<description><![CDATA[The whole purpose of refactoring is to make us program faster, producing more value with less effort. and But I think the most dangerous way that people get trapped is when they try to justify refactoring in terms of &#34;clean code&#34;, &#34;good engineering practice&#34;, or similar moral reasons. The point of refactoring isn't to show [&#8230;]]]></description>
										<content:encoded><![CDATA[<blockquote>
<p>The whole purpose of refactoring is to make us program faster, producing more value with less effort.</p>
</blockquote>
<p>and</p>
<blockquote>
<p>But I think the most dangerous way that people get trapped is when they try to justify refactoring in terms of &quot;clean code&quot;, &quot;good engineering practice&quot;, or similar moral reasons. The point of refactoring isn't to show how sparkly a code base is -- it is purely economic. We refactor because it makes us faster -- fastor add features, faster to fix bugs.</p>
</blockquote>
<p>-- From <em>Refactoring: Improving the Design of Existing Code</em> (Martin Fowler and Kent Beck), page 56</p>
]]></content:encoded>
					
					<wfw:commentRss>https://rafael.bernard-araujo.com/principles-in-refactoring-slowing-down-new-features.php/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2252</post-id>	</item>
		<item>
		<title>The Rule of Three</title>
		<link>https://rafael.bernard-araujo.com/the-rule-of-three.php</link>
					<comments>https://rafael.bernard-araujo.com/the-rule-of-three.php#respond</comments>
		
		<dc:creator><![CDATA[rafael]]></dc:creator>
		<pubDate>Sun, 19 Oct 2025 19:56:20 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[software engineering]]></category>
		<guid isPermaLink="false">https://rafael.bernard-araujo.com/?p=2243</guid>

					<description><![CDATA[The first time you do something, you just do it. The second time you do something similar, you wince at the duplication, but you do the duplicate thing anyway. The third time you do something similar, you refactor. -- Don Roberts]]></description>
										<content:encoded><![CDATA[<blockquote>
<p>The first time you do something, you just do it. The second time you do something similar, you wince at the duplication, but you do the duplicate thing anyway. The third time you do something similar, you refactor.</p>
</blockquote>
<p>-- Don Roberts</p>
]]></content:encoded>
					
					<wfw:commentRss>https://rafael.bernard-araujo.com/the-rule-of-three.php/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2243</post-id>	</item>
		<item>
		<title>Tropeçando 113</title>
		<link>https://rafael.bernard-araujo.com/tropecando-113.php</link>
					<comments>https://rafael.bernard-araujo.com/tropecando-113.php#respond</comments>
		
		<dc:creator><![CDATA[rafael]]></dc:creator>
		<pubDate>Fri, 16 Aug 2024 05:13:13 +0000</pubDate>
				<category><![CDATA[Tropeçando]]></category>
		<category><![CDATA[aws]]></category>
		<category><![CDATA[aws-cdk]]></category>
		<category><![CDATA[clean architecture]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[ddd]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[serverless]]></category>
		<category><![CDATA[software engineering]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[sqli]]></category>
		<guid isPermaLink="false">https://rafael.bernard-araujo.com/?p=1976</guid>

					<description><![CDATA[Neon Serverless PostgreSQL database with real zero-scaling. The fully managed serverless Postgres with a generous free tier. We separate storage and compute to offer autoscaling, branching, and bottomless storage. Compute scales dynamically to ensure you're ready for peak hours. Compute scales to zero and cold storage offloads to S3 for cost efficiency. Create a fully [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><a href="https://neon.tech">Neon</a></p>
<blockquote>
<p>Serverless PostgreSQL database with real zero-scaling. The fully managed serverless Postgres with a generous free tier. We separate storage and compute to offer autoscaling, branching, and bottomless storage.</p>
<p>Compute scales dynamically to ensure you're ready for peak hours. Compute scales to zero and cold storage offloads to S3 for cost efficiency. Create a fully managed serverless Postgres instance in seconds.</p>
</blockquote>
<p><a href="https://laravel-news.com/make-your-app-faster-with-php-83">Make your app faster with PHP 8.3</a></p>
<blockquote>
<p>PHP 8.3 is the latest version of PHP. It has exciting new features and major improvements in performance. By upgrading to 8.3, you can achieve a significant increase in speed. In this article, we dive into how PHP 8.3 can be a game changer. It can speed up your application's performance.</p>
</blockquote>
<p><a href="https://dzone.com/articles/owasp-top-10-explained-3-sql-injection?">OWASP Top 10 Explained: SQL Injection</a></p>
<blockquote>
<p>SQL Injection (SQLi) is a code injection technique that exploits a security vulnerability occurring in the database layer of an application.</p>
<p>The vulnerability is present when user inputs are either improperly filtered for string literal escape characters embedded in SQL statements or user input is not strongly typed and thereby unexpectedly executed.</p>
<p>This allows an attacker to manipulate SQL queries, enabling them to unauthorized access, modify, and delete data in the database. This can lead to significant breaches of confidentiality, integrity, and availability, ranging from unauthorized viewing of data to complete database compromise.</p>
</blockquote>
<p><a href="https://blog.serverlessadvocate.com/15-quick-useful-tips-for-aws-cdk-engineers-a7675e1557aa">15 Quick Useful Tips for AWS CDK Engineers</a></p>
<blockquote>
<p>In this short article, we will cover 15 useful tips with accompanying code snippets for AWS CDK users.</p>
</blockquote>
<p><a href="https://khalilstemmler.com/articles/typescript-domain-driven-design/repository-dto-mapper/">Implementing DTOs, Mappers &amp; the Repository Pattern using the Sequelize ORM [with Examples] - DDD w/ TypeScript</a></p>
<blockquote>
<p>There are several patterns that we can utilize in order to handle data access concerns in Domain-Driven Design. In this article, we talk about the role of DTOs, repositories &amp; data mappers in DDD.</p>
</blockquote>
]]></content:encoded>
					
					<wfw:commentRss>https://rafael.bernard-araujo.com/tropecando-113.php/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1976</post-id>	</item>
		<item>
		<title>Notes &#8211; ServerlessDays NZ 2024</title>
		<link>https://rafael.bernard-araujo.com/notes-serverlessdays-nz-2024.php</link>
					<comments>https://rafael.bernard-araujo.com/notes-serverlessdays-nz-2024.php#respond</comments>
		
		<dc:creator><![CDATA[rafael]]></dc:creator>
		<pubDate>Wed, 05 Jun 2024 23:57:13 +0000</pubDate>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[aws]]></category>
		<category><![CDATA[conference]]></category>
		<category><![CDATA[serverless]]></category>
		<category><![CDATA[software architecture]]></category>
		<category><![CDATA[software engineering]]></category>
		<guid isPermaLink="false">https://rafael.bernard-araujo.com/?p=2011</guid>

					<description><![CDATA[Those are my notes for ServelessDays NZ - Auckland, at 24th May 2024. Sheen Brisals - Think, Architect, and Build Serverless Applications as Set Pieces During ServerlessDaysNZ Sheen Brisals gave the talk Think, Architect, Build, Sustain Serverless Application Set Pieces. It was full of important insights to Set Pieces and sustain Serverless Applications. I particularly [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Those are my notes for <a href="https://anz.serverlessdays.io/auckland/">ServelessDays NZ - Auckland, at 24th May 2024</a>.</p>
<h2>Sheen Brisals - Think, Architect, and Build Serverless Applications as Set Pieces</h2>
<p>During <a href="https://lnkd.in/gsACxdcY">ServerlessDaysNZ</a> Sheen Brisals gave the talk Think, Architect, Build, Sustain Serverless Application Set Pieces. It was full of important insights to Set Pieces and sustain Serverless Applications.</p>
<p>I particularly liked how he touched on the fact that legacy applications being rewritten to Serverless is a thing, as this is everywhere being part of lots of engineers' lives.</p>
<p>More than that, Brisals highlighted how patterns and pivotal for a maintainable and reliable application, despite the execution model:</p>
<ul>
<li>Identify Domains so you can decouple a domain to rewrite it more effectively</li>
<li>Complexity is better abstracted, becoming simpler, when you know and apply good proven Patterns -- the exception is to invent a new one</li>
<li>Design Patterns, Architecture Patterns, Execution Model patterns, Software Design, etc, will improve the quality of your Application. As Serverless will likely push you to learn them, you have the opportunity to develop as an Architect</li>
<li>The Serverless should help you to think in the whole picture, as the settled pieces need communication between them, therefore optimising value to the end-user</li>
</ul>
<p>Unfortunately, I was not selected to win the book Serverless Development on AWS, but for those who won, I wish they could learn a lot there. What a great indication of how good a fellow is Sheen. Giving away those books is a gigantic contribution to the community!</p>
<p>I am very pleased to know you in person, Sheen.</p>
<p>This presentation talked a lot with Michael Walmsley's. So nice.</p>
<h2>Heitor Lessa - Let Them Retry: Idempotency for the Rest of Us</h2>
<p>Despite being common to talk or to assess if a given application or infrastructure follows best practices and great architectural patterns, implementing this is a challenge for development teams for different reasons.</p>
<p>Heitor Lessa, in his talk <a href="https://lnkd.in/giRTzEvx">&quot;Let Them Retry: Idempotency for the Rest of Us&quot;</a>, demonstrates how a tool that improves the Developer Experience bringing the implementation of the patterns close to the code is powerful to win adoption. PowerTools is a developer toolkit to accelerate development providing interfaces and abstractions to implement Serverless best practices.</p>
<p>Heitor used a sample code, emulating an existent codebase, from an application already working in Production. We had the opportunity to see the appeal of <a href="https://github.com/aws-powertools/">PowerTools</a>. Usually, Idempotency (to handle duplicated transactions) is associated with a good amount of change in the code. Still, PowerTools was designed to introduce no or very few impacts to a code that is very dangerous to change. As building blocks, adding more complex functionalities, such as caching, payload tempering and failure mode.</p>
<p>The existence of tools like PowerTools reinforces how implementing good and proven software (and architectural) patterns is pivotal for a scalable and reliable application. The Serverless execution mode can mislead to relaxed code, but that would weaken the performance and stability of an application. The lesson is that working smarter is applying known solutions for specific problems.</p>
<p>PowerTools provides a wide range of functionalities, not surprisingly being able to match Well-Architected frameworks in their implementation: Secrets/System Manager Parameters, Event Source Data Classes, Validation, Feature Flag, Idempotency, Data Masking, Streaming, Middleware, JMESPath, Batch processing, Metrics, Tracing. We avoid writing boilerplates, repeated code and even the need to create a shared lib of constructs ourselves. The community is improving it.</p>
<p>PowerTools is a helpful tool to implement these features. This is an opportunity to learn and deep dive into best practices and designs. It also enhances how you observe and monitor your application. It is a serious tool to consider if you intend to leverage how your code is executed, deployed, monitored and performed.</p>
<p>In his talk, Heitor implemented, live in the meeting, Idempotency into a legacy code. He enriched it with failure modes, caching, payload tampering and order tolerance. So, PowerTools is also very easy and quick to use.</p>
<blockquote>
<p>Best practices for everyone</p>
<ul>
<li>Heitor Lessa</li>
</ul>
</blockquote>
<h2>Michael Walmsley - Unleashing Serverless Scalability on AWS: Practical Strategies and Proven Patterns</h2>
<p>Some started Michael Walmsley introduction saying &quot;A fantastic human being...&quot;. And I will start from there as well because I have experienced that myself.</p>
<p>I bumped into Michael while walking to the conference venue. I first heard about it from a great friend, Joshua Katz, who was impressed with Michael. It was a very pleasant walk while sharing quick impressions of being AWS Community Builders and excitement about the conference.</p>
<p>It happens that Michael is now an AWS Hero with many years of experience to share. One of the first things he said in his talk was replaying Suzana Melo Moraes (you should listen to this girl - so inspiring), who has three years in tech, when she was saying that, mostly every day, she struggles with something usually starting from having no idea how to fix a particular problem she was assigned to solve. Michael sympathised, saying that, even after 30 years, there are days that things happen to him the same way. This happens in everyone involved in this field and it was so humbling coming from him.</p>
<p>As usual, Michael doesn't keep secrets by himself but shares insightful tips. His presentation was about Unleashing Serverless Scalability on AWS:</p>
<ul>
<li>Start the design with the needed scalability in mind (can you see that links to Sheen Brassals talk?)</li>
<li>Master and understand well the limits, they are there for a reason and as early you design your application to work with them, better design your application and scalable-ready it is</li>
<li>Events, Messages, and Commands are the way of communication for Serverless and a must-know subject</li>
<li>Do not ignore Flow Control</li>
<li>Break your application limits before someone else does -- use performance tests in your favour</li>
<li>Study and use proven patterns (check <a href="https://serverlessland.com">https://serverlessland.com</a>)</li>
</ul>
<h2>Brad Jacques - Delivering at pace while evolving a Serverless architecture</h2>
<p><a href="https://www.linkedin.com/in/bradjacques/">Brad Jacques</a> delivered a talk titled <a href="https://lnkd.in/grFK_7ND">&quot;Delivering at pace while evolving a Serverless architecture&quot;</a> at ServerlessDays NZ. Brad covered a challenging project where file manipulation use cases were an important feature. </p>
<p>&quot;Complexity is everywhere&quot;. Brad could not help it advise that a successful delivery starts from breaking the complexity into pieces, to plan ahead of time and to <strong>do the simple things first</strong>. He mentioned that the deadline was short, affirming it was the right strategy to evolve the architecture.</p>
<p>He also stressed the use of established patterns for success, such as breaking down complexity, identifying domains and context boundaries, and understanding limits and messaging.</p>
<p>It was also important how the work was planned with the team. Having a small committed team, fast feedback loops and continuous measurement were key to proving the solution was correct.</p>
<p>The summary is so great that I will copy it here entirely:</p>
<ul>
<li>Do the simple thing first</li>
<li>Small teams with a fast feedback loop (showcase often)</li>
<li>Identify risk early, shift left, and spike</li>
<li>Continuously measure performance, and stress test</li>
<li>Isolate context boundaries</li>
<li>The solution must prove itself correct</li>
</ul>
<p>Brad's insights were based on his experience with a new project for a major client at a consultancy company. However, it was clear that the principles and strategies he shared apply to any application, in any industry, and of any size.</p>
<p>His parting advice was to &quot;evolve your architecture, measure, and make decisions throughout the process.&quot;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://rafael.bernard-araujo.com/notes-serverlessdays-nz-2024.php/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2011</post-id>	</item>
		<item>
		<title>Tropeçando 111</title>
		<link>https://rafael.bernard-araujo.com/tropecando-111.php</link>
					<comments>https://rafael.bernard-araujo.com/tropecando-111.php#respond</comments>
		
		<dc:creator><![CDATA[rafael]]></dc:creator>
		<pubDate>Mon, 30 Oct 2023 07:37:05 +0000</pubDate>
				<category><![CDATA[Tropeçando]]></category>
		<category><![CDATA[business]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[software architecture]]></category>
		<category><![CDATA[software engineering]]></category>
		<guid isPermaLink="false">https://rafael.bernard-araujo.com/?p=1851</guid>

					<description><![CDATA[Don't do this: creating useless indexes This is why, when I’m called for a performance problem (or for an audit), my first take is to look at the size of the data compared to the size of the indexes. If you store more indexes than data for a transactional workload, that’s bad. The worst I’ve [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><a href="https://web.archive.org/web/20240525164456/https://mydbanotebook.org/post/too-many-indexes/">Don't do this: creating useless indexes</a></p>
<blockquote><p>
This is why, when I’m called for a performance problem (or for an audit), my first take is to look at the size of the data compared to the size of the indexes. If you store more indexes than data for a transactional workload, that’s bad. The worst I’ve seen was a database with 12 times more indexes stored on disk than data! Of course, it was a transactional workload… Would you buy a cooking book with 10 pages of recipes and 120 pages of indexes at the end of the book?</p>
<p>The problem with indexes is that each time you write (insert, update, delete), you will have to write to the indexes too! That can become very costly in resources and time.
</p></blockquote>
<p><a href="http://blog.cleancoder.com/uncle-bob/2023/01/18/functional-classes.html">Functional Classes</a></p>
<blockquote>
<blockquote><p>
A place for everything, and everything in its place.
</p></blockquote>
<p>What is a class? According to the dictionary a class is:</p>
<blockquote><p>
A set, collection, group, or configuration containing members regarded as having certain attributes or traits in common; a kind or category.
</p></blockquote>
</blockquote>
<p><a href="https://frederickvanbrabant.com/blog/2019-04-03-the-simple-class/">The Simple Class</a></p>
<blockquote><p>
I work in many legacy code bases, and in fact, I’ve made it a big part of my career. I love diving into big monoliths that have grown out of proportion and tidying them up. One of the best parts of that work is rewriting a God class into a collection of small reusable classes. Let’s take a look at what makes a simple class great.
</p></blockquote>
<p><a href="https://frederickvanbrabant.com/blog/2020-02-07-the-economics-of-clean-code/">The economics of clean code</a></p>
<blockquote><p>
Code smarter. Code balanced. That is OK to have some debt. But pay them off quickly.
</p></blockquote>
]]></content:encoded>
					
					<wfw:commentRss>https://rafael.bernard-araujo.com/tropecando-111.php/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1851</post-id>	</item>
	</channel>
</rss>
