<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alejandro Navas</title>
    <description>The latest articles on DEV Community by Alejandro Navas (@caeus).</description>
    <link>https://dev.to/caeus</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2014181%2F4a426789-1ff3-48c0-941b-25968d5f5c32.png</url>
      <title>DEV Community: Alejandro Navas</title>
      <link>https://dev.to/caeus</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/caeus"/>
    <language>en</language>
    <item>
      <title>wyr: Typesafe Dependency Injection for TypeScript (and why I built it)</title>
      <dc:creator>Alejandro Navas</dc:creator>
      <pubDate>Sat, 04 Oct 2025 16:13:04 +0000</pubDate>
      <link>https://dev.to/caeus/wyr-typesafe-dependency-injection-for-typescript-and-why-i-built-it-45kh</link>
      <guid>https://dev.to/caeus/wyr-typesafe-dependency-injection-for-typescript-and-why-i-built-it-45kh</guid>
      <description>&lt;p&gt;I’ve wired up enough services in TypeScript to know the pain of the usual contenders: sprawling globals, decorator-heavy DI containers, and hand-rolled factories that grow brittle with every new dependency. Wyr is the tool I always wished existed: a stateless, typesafe registry that treats dependency wiring as data. Here’s the philosophy, how it compares, and why it’s now my default.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Contenders (and why they fall short)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Global scope “service registries”&lt;/strong&gt; – Toss everything onto &lt;code&gt;globalThis&lt;/code&gt;, hope the import order is kind, and pray Next.js doesn’t optimize something away. It’s fast to throw together but impossible to reason about. You can’t diff it, you can’t snapshot it, and tests become a tightrope walk over ambient state.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;NestJS-style containers&lt;/strong&gt; – Great if you love classes and decorators. Not so great when you need value-based services, dynamic wiring, or precise types. Mocks must match classes, scoping rules are opaque, and circular dependencies surface at runtime with stack traces from the abyss.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Manual wiring&lt;/strong&gt; – Still better than the other two: at least you &lt;em&gt;know&lt;/em&gt; what’s happening. But it’s death by a thousand constructors. Every refactor means touching multiple files, and there’s zero compile-time help to stop you from forgetting a dependency. Tests become a copy-paste festival.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted the consistency of declarative wiring with the clarity of explicit code. Something I could diff, test, and reason about without magic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Philosophy: registries, not containers
&lt;/h2&gt;

&lt;p&gt;wyr is a registry of bindings. That’s it. No hidden scope, no lifecycle annotations, no mysterious module graph. You describe a dependency as a tuple of keys, and TypeScript ensures the graph is valid before you ever run the test suite.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pure data&lt;/strong&gt; – Behind the scenes every binding is just &lt;code&gt;{ deps, factory }&lt;/code&gt;. Cloning a registry is a spread operation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No runtime mutation&lt;/strong&gt; – Each bind returns a new registry; nothing mutates under your feet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graphs over containers&lt;/strong&gt; – Dependencies are explicit edges. You can inspect them, merge them, or snapshot them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic wiring&lt;/strong&gt; – Resolution is a single function that walks the graph, validates it, and resolves nodes in parallel.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s the inversion-of-control model I wanted: explicit graphs, compile-time guarantees, zero hidden global state.&lt;/p&gt;




&lt;h2&gt;
  
  
  Safety baked in
&lt;/h2&gt;

&lt;p&gt;Two things still give me a little thrill every time I run them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compile-time missing dependency detection&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
If you bind an API that depends on &lt;code&gt;repo$&lt;/code&gt;, TypeScript will refuse to compile until &lt;code&gt;repo$&lt;/code&gt; is bound. You see the missing key, the context, and the offending registry &lt;em&gt;before&lt;/em&gt; you open Vitest.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compile-time cycle detection&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Accidentally create a loop when refactoring? You get a descriptive type error that points to the cycle. No more runtime deadlocks or stack overflows.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All of this lives in the type system—no custom lint rules, no code generation.&lt;/p&gt;




&lt;h2&gt;
  
  
  The fresh take
&lt;/h2&gt;

&lt;p&gt;Wyr intentionally leaves out the trappings of traditional containers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No scopes, modules, providers, beans, or decorators.&lt;/li&gt;
&lt;li&gt;No eager singletons or lifecycle annotations.&lt;/li&gt;
&lt;li&gt;No global container instance to mutate (or forget to reset in tests).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You get four core operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;bind().toValue()&lt;/code&gt; – lock in a concrete value.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bind().toFunction([...deps], fn)&lt;/code&gt; – describe how to build a service given dependencies.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wire&lt;/code&gt; / &lt;code&gt;wireTuple&lt;/code&gt; / &lt;code&gt;wireRecord&lt;/code&gt; – resolve services deterministically.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;snapshot([...keys])&lt;/code&gt; – materialize a subset into a standalone registry with no dependencies.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s the entire API surface. Easy to teach, easy to audit, easy to test.&lt;/p&gt;




&lt;h2&gt;
  
  
  The people who pushed me to ship it
&lt;/h2&gt;

&lt;p&gt;Wyr wouldn’t exist without a handful of TypeScript “typelevel magicians” I met in the community—folks who maintain their own libraries, routinely bend the type system to their will, and even ship PRs straight into TypeScript itself. Watching them operate made me want to contribute something that felt equally deliberate and uncompromising. Wyr is my attempt to join that conversation: a library that leans on the type system, explains itself through data, and holds up under scrutiny.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I built it (for me)
&lt;/h2&gt;

&lt;p&gt;Every project that outgrew the “just import a module” phase seemed to devolve into container chaos or brittle wiring scripts. I wanted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A way to prove the dependency graph was sound at compile time.&lt;/li&gt;
&lt;li&gt;Declarative wiring that read like a build plan, not a constructor chain.&lt;/li&gt;
&lt;li&gt;Testability without ceremony: spinning up a registry for a spec should be a one-liner.&lt;/li&gt;
&lt;li&gt;Deterministic, parallel resolution without sacrificing clarity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;wyr is the result. I’ve been using it to assemble services that are simultaneously flexible and predictable. When I snapshot a subset of the graph for a microservice test harness or a CLI utility, I know exactly what’s included and how it was built.&lt;/p&gt;




&lt;h2&gt;
  
  
  Want to try it?
&lt;/h2&gt;

&lt;p&gt;Grab it on npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;wyr-ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Define your keys, bind your services, and let TypeScript keep you honest. The README and GitHub Wiki cover getting started, architecture, and how to snapshot subgraphs for tests.&lt;/p&gt;

&lt;p&gt;No more “wait, where is this actually coming from?” Just registries, graphs, and confidence.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>typescript</category>
      <category>tooling</category>
      <category>dependencyinjection</category>
    </item>
    <item>
      <title>The Cult of Shipping Fast Is Killing Your Code</title>
      <dc:creator>Alejandro Navas</dc:creator>
      <pubDate>Fri, 29 Aug 2025 14:25:53 +0000</pubDate>
      <link>https://dev.to/caeus/the-cult-of-shipping-fast-is-killing-your-code-oc</link>
      <guid>https://dev.to/caeus/the-cult-of-shipping-fast-is-killing-your-code-oc</guid>
      <description>&lt;p&gt;Most developers are mediocre.&lt;br&gt;
Not average. Mediocre. As in: they write code that only works because the universe hasn’t gotten around to punishing them yet. Half the systems you depend on right now are standing on a pile of duct tape, broken abstractions, and blind faith, and the people who built them are still out here bragging about “moving fast.”&lt;/p&gt;

&lt;p&gt;I get why it happens. Startups train you to ship first and think later. That’s fine when you’ve got five people, no users, and two months of runway. But developers never outgrow it. The company scales, the codebase mutates into an unholy swamp of side effects and “temporary” hacks, and they just… keep going. Those clever shortcuts that were supposed to last a week are still running in prod three years later. Those “we’ll refactor this later” tickets are fossilized in Jira. Nobody remembers how half the thing works, but hey, at least we launched another feature on time.&lt;/p&gt;

&lt;p&gt;And now we’ve added AI into the mix, which is like giving a chainsaw to someone who can’t cut straight with scissors. Tools like Copilot, Cursor, Claude, and GPT are trained on the same codebases written by these same developers, which means they’ve absorbed every bad habit the industry has normalized. You ask for “clean, maintainable code” and it confidently spits out something that looks fine, compiles fine, and quietly plants a landmine under your future self’s desk.&lt;/p&gt;

&lt;p&gt;AI doesn’t know your architecture, your invariants, your scaling bottlenecks, or the nightmare edge cases that keep you up at night. It just predicts what code “probably” looks like. And since most code out there is mediocre, what you’re getting is mediocre code at lightspeed. Polished junk. Garbage with good indentation.&lt;/p&gt;

&lt;p&gt;The real punchline is watching developers treat AI like it’s a genius senior engineer instead of what it actually is: autocomplete with a god complex. They stop questioning it. They stop thinking. They just hit Tab and move on. The AI mirrors their bad habits, they feed those habits back into the system, and the next generation of AI learns from that garbage. Congrats, you’ve built a self-replicating mediocrity machine.&lt;/p&gt;

&lt;p&gt;And here’s the part nobody wants to admit: writing good software is hard. It’s supposed to be hard. It takes thought. It takes restraint. It takes looking at the clever trick your AI just suggested and saying, “That’s going to bite someone in six months,” and writing the boring, obvious version instead. But the industry doesn’t reward that. The industry rewards speed. It celebrates shipping. It fetishizes “impact.” And then, when the system inevitably collapses under its own weight, everyone acts shocked — as if we didn’t all see it coming.&lt;/p&gt;

&lt;p&gt;AI isn’t going to fix this. It doesn’t know the difference between elegant and awful. It gives you what “most developers would do,” which is exactly the problem. That’s why your codebase already has five half-broken auth flows, three duplicated JSON parsers, two abandoned feature flags, and a migration script from last year that nobody dares to run because nobody remembers what it does.&lt;/p&gt;

&lt;p&gt;If you want better software, stop coding like everyone else. Stop trusting an autocomplete engine to think for you. Slow down. Understand what you’re building. Write code your future self won’t want to delete on sight.&lt;/p&gt;

&lt;p&gt;Because if you don’t, we’re headed for a future where developers don’t even write bad code anymore. AI writes bad code for them. Faster. At scale. With silly tests. And somehow, we’ll still be standing around asking why everything keeps breaking.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mediocrity</category>
      <category>cleancode</category>
      <category>rant</category>
    </item>
    <item>
      <title>DDD Without the Tactical Patterns</title>
      <dc:creator>Alejandro Navas</dc:creator>
      <pubDate>Mon, 04 Aug 2025 10:11:55 +0000</pubDate>
      <link>https://dev.to/caeus/ddd-without-the-tactical-patterns-4320</link>
      <guid>https://dev.to/caeus/ddd-without-the-tactical-patterns-4320</guid>
      <description>&lt;h2&gt;
  
  
  Or: Why Suffixing Classes &lt;em&gt;Entity&lt;/em&gt; Doesn't Make You Strategic
&lt;/h2&gt;

&lt;p&gt;Let’s get straight to the point: most people doing "DDD" aren't doing Domain-Driven Design. They're naming classes after the tactical patterns in the book. That's not DDD. That’s cosplay.&lt;/p&gt;

&lt;p&gt;I say this not to gatekeep, but to clarify: Domain-Driven Design isn’t a checklist of patterns. It’s a mindset. A strategy. A way of seeing software not as code, but as a &lt;strong&gt;model of a shared understanding&lt;/strong&gt; between domain experts and developers. The moment we reduce that to “put your Repos here, Entities there, call it an Aggregate Root and go home,” we've missed the whole point.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Tactical Pattern Trap
&lt;/h3&gt;

&lt;p&gt;The original DDD book introduced terms like &lt;em&gt;Entity&lt;/em&gt;, &lt;em&gt;Value Object&lt;/em&gt;, &lt;em&gt;Aggregate&lt;/em&gt;, &lt;em&gt;Repository&lt;/em&gt;, and &lt;em&gt;Service&lt;/em&gt; to help &lt;em&gt;express&lt;/em&gt; your domain model. But somewhere along the way, these names became an end in themselves. &lt;/p&gt;

&lt;p&gt;People slap "Repository" on a class that just wraps a data access layer and think it’s DDD. They create folders called "Aggregates" but couldn't tell you what makes a &lt;em&gt;good&lt;/em&gt; aggregate boundary. That’s DDD jargon, when we should be using our domain's specific jargon (hint: Ubiquitous Language)&lt;/p&gt;

&lt;p&gt;These tactical patterns are useful, but only when used in service of the model. Not as decorative labels.&lt;/p&gt;

&lt;p&gt;Here’s a tough question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When was the last time you worked directly with a domain expert to shape your model?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not &lt;em&gt;stakeholders&lt;/em&gt;. Not &lt;em&gt;product&lt;/em&gt;. I mean someone who lives and breathes the problem space. Someone who would say, “No, that’s not how first in human trials work” and help you fix the mental model.&lt;/p&gt;

&lt;p&gt;If that’s not happening, it doesn’t matter how many “Aggregates” you have. You’re not doing DDD.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Heart of DDD is strategic, not tactical.
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;strategic&lt;/em&gt; patterns in DDD—&lt;strong&gt;Bounded Contexts&lt;/strong&gt;, &lt;strong&gt;Ubiquitous Language&lt;/strong&gt;, &lt;strong&gt;Context Maps&lt;/strong&gt;—are where the real power lies. They're also the parts that rarely make it into your code editor.&lt;/p&gt;

&lt;p&gt;Why? Because they’re hard. They require conversations, missteps, negotiation. They expose organizational friction. And most of all, they demand that developers &lt;em&gt;care&lt;/em&gt; about the business, not just the system.&lt;/p&gt;

&lt;p&gt;But they also unlock clarity. Once you understand where your boundaries lie, you stop trying to model the whole business in one place. Once you share language with domain experts, your code starts making &lt;em&gt;sense&lt;/em&gt;. Not to just other developers, but to the people who matter.&lt;/p&gt;

&lt;p&gt;And here’s the kicker: you can do all of that without ever using the suffix "Repo".&lt;/p&gt;




&lt;h3&gt;
  
  
  So What Should You Do Instead?
&lt;/h3&gt;

&lt;p&gt;Forget the patterns for a moment. Start here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Talk to a domain expert.&lt;/strong&gt; Not just once. Regularly. Let them poke holes in your model. Be wrong. That’s part of it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use their language.&lt;/strong&gt; Literally. In code. If they call it a "Serious Adverse Event", you call it that too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Draw the line.&lt;/strong&gt; Define your Bounded Contexts. Don't mix vocabulary from different subdomains just because it’s convenient.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Think in terms of &lt;em&gt;behavior&lt;/em&gt;, not data.&lt;/strong&gt; What does this model &lt;em&gt;do&lt;/em&gt;, not just what does it &lt;em&gt;store&lt;/em&gt;?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolate the technical noise.&lt;/strong&gt; As developers, we deal with plenty of technical subdomains: SQL queries, serialization, HTTP, queues, and more. These are unavoidable, but respecting the spirit of DDD means ensuring the domain model isn't polluted by them. Let the glue code live at the edges, and keep your abstractions clean—no leaks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If later, your model starts to &lt;em&gt;look like&lt;/em&gt; the tactical patterns, great. But let them &lt;em&gt;emerge&lt;/em&gt;, don’t force them. They will, if you keep complexity in manageable doses.&lt;/p&gt;




&lt;h3&gt;
  
  
  Final Thought: DDD Is a Discipline, Not a Template
&lt;/h3&gt;

&lt;p&gt;DDD isn't about how your packages are structured. It’s about &lt;strong&gt;how you think&lt;/strong&gt; about your code and how closely that thinking aligns with the real-world domain you’re modeling.&lt;/p&gt;

&lt;p&gt;So next time you see a class called &lt;code&gt;StudyAggregateRoot&lt;/code&gt;, ask yourself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What makes this an aggregate? What invariant is it protecting? What part of the domain logic does this model embody?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you can answer that clearly, you’re on the right path.&lt;br&gt;&lt;br&gt;
If not, maybe it’s time to put the patterns down and go talk to someone who actually knows what a "Study" &lt;em&gt;does&lt;/em&gt;.&lt;/p&gt;

</description>
      <category>ddd</category>
      <category>cleancode</category>
      <category>architecture</category>
      <category>eventdriven</category>
    </item>
    <item>
      <title>My time on GDX/Undertrail</title>
      <dc:creator>Alejandro Navas</dc:creator>
      <pubDate>Wed, 16 Apr 2025 15:24:48 +0000</pubDate>
      <link>https://dev.to/caeus/my-time-on-gdxundertrail-3mlf</link>
      <guid>https://dev.to/caeus/my-time-on-gdxundertrail-3mlf</guid>
      <description>&lt;p&gt;When I joined the company in September 2015, the cracks were already showing—not in the people, but in the foundation. The software was a tangled mess, barely holding together under the weight of the company’s rapid growth. I was the first in-house developer brought on to fix it, and though leadership at the time was stable—even supportive—the real battle wasn’t with management. It was with the code itself.&lt;/p&gt;

&lt;p&gt;For months, I fought to untangle what felt like an impossible knot. But some systems aren’t meant to be salvaged. Slowly, almost instinctively, I began redesigning rather than repairing. I didn’t realize it then, but I was stepping into the role of an architect—not by title, but by necessity. And when new developers joined, they followed the structure I laid out, not because I imposed it, but because it made sense.&lt;/p&gt;

&lt;p&gt;For a while, things worked. The team thrived. The company thrived. We weren’t just fixing problems; we were building something sustainable.&lt;/p&gt;

&lt;p&gt;Then came the first misstep.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Wrong Tools and the Wrong Decisions
&lt;/h3&gt;

&lt;p&gt;Leadership—under pressure to scale—latched onto a competitor’s playbook: &lt;em&gt;No need to reinvent the wheel&lt;/em&gt;. They brought in off-the-shelf tools meant to streamline operations. In theory, it was sound. In practice, it was a disaster.&lt;/p&gt;

&lt;p&gt;These tools weren’t built for us. They warped our processes, forced awkward workarounds, and—worst of all—gave non-technical leaders the illusion of control. The more we relied on them, the more fragile everything became. Predictably, it backfired. A major failure cost the company dearly, and suddenly, everyone was scrambling for solutions.&lt;/p&gt;

&lt;p&gt;This crisis proved the pattern: when things broke badly enough, my architectural instincts could provide the way forward. The system I proposed wasn't revolutionary, just carefully tailored to our actual needs rather than chasing competitors. And like before, it worked - operations stabilized, and for a brief, golden period, we were untouchable.&lt;/p&gt;

&lt;p&gt;Then the hierarchy shifted.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Fall of Camilo and the Rise of Silvia
&lt;/h3&gt;

&lt;p&gt;Camilo Jiménez, the CTO, had been a steady hand. He wasn’t perfect, but he understood the balance between technical needs and business demands. His relationship with the CEO, however, had been deteriorating for months. When he left, it wasn’t just a leadership gap—it was a power vacuum.&lt;/p&gt;

&lt;p&gt;Silvia, the PMO, slid into his role. Not because she was the best choice, but because she was there.&lt;/p&gt;

&lt;p&gt;Her management style was rigid, her technical understanding superficial. She saw deadlines, not systems; authority, not collaboration. Disagreeing with her wasn’t just dissent—it was disrespect. Tasks were assigned without context, decisions made without consultation, and the fragile stability we’d built began to crack under the weight of short-term thinking.&lt;/p&gt;

&lt;p&gt;I pushed back. Hard.&lt;/p&gt;

&lt;p&gt;When she overruled technical decisions, I challenged her. When she prioritized speed over sustainability, I refused. It wasn’t rebellion—it was preservation. But to her, it was insubordination.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Breaking Point
&lt;/h3&gt;

&lt;p&gt;By July 2017, I’d had enough. I submitted my resignation.&lt;/p&gt;

&lt;p&gt;Then, a twist: a consultant—&lt;em&gt;el Chino&lt;/em&gt;—arrived. He echoed everything we’d been saying, reinforcing what Silvia had dismissed. For a moment, it seemed like sanity would prevail. I considered staying, but only if Silvia’s influence over technical decisions was curbed.&lt;/p&gt;

&lt;p&gt;She refused.&lt;/p&gt;

&lt;p&gt;To her, I wasn’t protecting the company—I was undermining her. And because she had the CEO’s ear, my fate was sealed.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Aftermath
&lt;/h3&gt;

&lt;p&gt;I wasn’t the first to leave. I wouldn’t be the last. But what stung most wasn’t the exit—it was knowing how easily avoidable it had been.&lt;/p&gt;

&lt;p&gt;The team fought for me. That mattered. They knew what Silvia didn’t: that good systems aren’t built by authority, but by trust.&lt;/p&gt;

&lt;p&gt;Less than a year later, Silvia was gone—quietly deemed unfit for the CTO role. Yet on her résumé, she claimed the company’s eventual acquisition was the reason for her departure. Lies.&lt;/p&gt;

&lt;p&gt;And if there’s a lesson in all of this, it’s that no amount of clever tools or rigid authority can replace the one thing that actually makes a company work—people who care enough to push back when it counts.&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>architecture</category>
      <category>anecdote</category>
      <category>growth</category>
    </item>
    <item>
      <title>Simplicity’s Irony: When inaccurate modeling creates needless complexity</title>
      <dc:creator>Alejandro Navas</dc:creator>
      <pubDate>Thu, 03 Apr 2025 09:25:14 +0000</pubDate>
      <link>https://dev.to/caeus/simplicitys-irony-when-inaccurate-modeling-creates-needless-complexity-p27</link>
      <guid>https://dev.to/caeus/simplicitys-irony-when-inaccurate-modeling-creates-needless-complexity-p27</guid>
      <description>&lt;p&gt;As you can tell, I'm obsessed with simplicity. Most articles I write talk about the problems of both over-engineering or under-engineering.&lt;/p&gt;

&lt;p&gt;During my whole career, however, I've stumbled upon more cases of under-engineering and oversimplification than the other way around.&lt;/p&gt;

&lt;p&gt;The question remains open: What's simple? And I've got to be honest. I don't know. A swarm of ideas compete in my head so instead today I'll let some of my experiences take the spotlight and grow into a lesson I hope other developers and engineers can synthesize. &lt;/p&gt;

&lt;h2&gt;
  
  
  Undertrail
&lt;/h2&gt;

&lt;p&gt;Back in 2015 I started working for a company called Undertrail (later acquired by Hopper).&lt;br&gt;
The company was growing at a very scary and promising pace. They attempted to become the &lt;a href="https://en.wikipedia.org/wiki/Global_distribution_system" rel="noopener noreferrer"&gt;GDS (Global Distribution System)&lt;/a&gt; for lowcost airlines (and other transport means) in Latam.&lt;/p&gt;

&lt;p&gt;Undertrail's solution was a Frankenstein's monster composed of bad decisions, messy workarounds, and unfortunate oversimplifications. It was a miracle its software even started. It was also no surprise that it was crashing more than once a day.&lt;/p&gt;

&lt;p&gt;I was their first in-house developer. Their solution was built by a freelancer. I was handed over that solution, and I was expected to take care of it as the company grew.&lt;/p&gt;

&lt;p&gt;To this day I thank Undertrail for the opportunity to work for them, and for the mess they had to work with, because otherwise I wouldn't have known how enjoyable is to solve those problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Providers and airlines are the same thing, ain't them?
&lt;/h3&gt;

&lt;p&gt;This was the first real problem I faced here. When trying to integrate a big provider of flight tickets (one whose name I don't remember but which is currently known as kiwi.com), I noticed a big blocker due to the fact that the freelancer guy had modeled ticket providers and airlines as the same thing.&lt;/p&gt;

&lt;p&gt;Including kiwi.com wasn't as straight forward as it should be due to the fact that it provided tickets for multiple airlines, some of them, already hardcoded into our solution.&lt;/p&gt;

&lt;p&gt;The solution involved lots of workarounds, but all of them going in the same direction: Rebuilding the understanding behind our code—Airlines and Ticket providers are two different concepts.&lt;/p&gt;

&lt;h3&gt;
  
  
  The simple booking
&lt;/h3&gt;

&lt;p&gt;Previous fix indirectly solved scalability issues, without the need to add more CPU or memory resources in the pot. After that, Undertrail identified an opportunity for horizontal growth. this time, the focus shifted to giving customers finer control over what are typically called additional services: extra legroom, unusually shaped luggage, inflight meals, and similar offerings.&lt;/p&gt;

&lt;p&gt;Then came the first snag: payments were tightly coupled with bookings (same table/collection, 1-to-1 relationship, same lifecycle)—a legacy domain quirk that refused to die. Rather than rethink the flawed model, developers patched around it. The result? Separate payment flows for bookings and add-ons… plus a Frankenstein edge case for combined payments.&lt;/p&gt;

&lt;p&gt;Thankfully, my proposal was adopted before extensive workarounds were implemented. By decoupling Payments from the Bookings, we ensured Payments had their own lifecycle, and could be seamlessly tied to any future products, and current ones.&lt;/p&gt;

&lt;h3&gt;
  
  
  The obvious Purchase Engine
&lt;/h3&gt;

&lt;p&gt;Undertrail had a team of what we called "The bookers". They were necessary to complete the lifecycle of the booking. Bookings were created, paid, processed and delivered. The processing part was completed by the bookers. It involved manually buying the purchased flights in some of our providers.&lt;/p&gt;

&lt;p&gt;Once we saw the opportunity to automate that part, developers mindlessly started providing solutions. The winning one: A new state in-between paid and processing. The result? Confused bookers, unclear lifecycle, double purchases ($$$), and more.&lt;/p&gt;

&lt;p&gt;The real solution: There's no new state. We just have a booker that instead of using the UI, uses the API, but does exactly the same: Gets quotes, picks the best, and buys the products. A new different problem, that didn't necessarily change how we processed bookings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Foodology
&lt;/h2&gt;

&lt;p&gt;Back in 2021 I was begging to the universe for the chance to work at a company that was growing real fast, yet with a solution that couldn't handle that growth. Foodology was that company.&lt;/p&gt;

&lt;p&gt;They operate hidden kitchens all over Latam, and their main source and only source of revenue, comes from selling food via food delivery platforms like UberEats, DoorDash and Rappi.&lt;/p&gt;

&lt;p&gt;Their solution was also a Frankenstein's monster. A monolith tailored to receive orders from our main provider (Rappi), and painfully patched to support other food delivery providers that came later.&lt;/p&gt;

&lt;h3&gt;
  
  
  So we sell via Rappi, right?
&lt;/h3&gt;

&lt;p&gt;When I arrived, I noticed their solution had a critical flaw: it reflected an oversimplified understanding of their business domain. They received and stored orders in their original format—without standardizing them across different food delivery platforms—and only processed the data when printing kitchen tickets. This approach implied that Foodology’s role ended once the ticket was printed, making it near impossible to extend the system with additional steps, such as billing or inventory synchronization.&lt;/p&gt;

&lt;p&gt;To improve that mess I suggested to create an agnostic receiver of orders (affectionately called the Cookie Monster by the team), implicitly challenging the original misconception that there are no steps between receiving an order, and sending it to the kitchen. Then we built "integrators" for each one of the platforms we integrated, each one translating from their representation of an order, to our representation of an order. That enabled the team to quickly integrate more platforms without the need to modify the core receiver.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decoupling, who cares?
&lt;/h3&gt;

&lt;p&gt;Our receiver worked, but handling the ever-changing tasks for each order—billing, delivery, inventory sync, kitchen tickets, etc.—was a mess.&lt;/p&gt;

&lt;p&gt;The naive solution? Chain tasks together, each triggering the next. Dozens of queues, ad-hoc error handling, and special cases for every order type (platform, pickup, location, etc.). It sucked. Adding or removing a task meant untangling dependencies, creating more queues, and hacking in exceptions.&lt;/p&gt;

&lt;p&gt;So I built an orchestrator. Provided a clear plan, it ensured each worker gets only what it needs, and cleans up after execution. No more queues, no tangled logic—just traceable flows and cleanly separated concerns.&lt;/p&gt;

&lt;h3&gt;
  
  
  It's just menu management
&lt;/h3&gt;

&lt;p&gt;At some point, when the team finally had some space to address some not so critical parts of the system, to reduce costs and improve efficiency, the team started working on automating the menu management.&lt;/p&gt;

&lt;p&gt;You know that whenever you use one of these food delivery apps, you can explore the menu of each of the restaurants in the neighborhood, right?&lt;/p&gt;

&lt;p&gt;Well, given the fact that Foodology had more than 100 kitchens, and in each kitchen we could sell about 10 different brands of food, and considering that we had already integrated about 10 different food delivery platforms. How many stores (and consequently menus) did we have to manage? 10k, approximately, right?&lt;/p&gt;

&lt;p&gt;Well, the provided solution ended up being a webapp in which you uploaded an Excel file modeling the menu, and manually selected the stores in which you wanted to place that menu... simple.&lt;/p&gt;

&lt;p&gt;Solution proved clunkier than anticipated. Implementation was very naive. You read the file, and then you synchronize the menu in all the selected stores.&lt;/p&gt;

&lt;p&gt;But then, given each platform is different, how do you manage the fact that each platform has a different API? Do you have to upload the file again if you want to reuse it later? What about checking the current status of a store? Or what happens in the case of failure during synchronization?&lt;/p&gt;

&lt;p&gt;When I turned my eye in that direction, I persuaded the team to re think the flow. When we came with a better solution, product people asked me "why so complex? Isn't it just menu management?"&lt;/p&gt;

&lt;p&gt;Well, the proposed solution was composed of three components&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A menu manager: a simple crud for adding and modifying menus and menu drafts.&lt;/li&gt;
&lt;li&gt;A menu assigner service: menus and stores are different. How are they related to each other? This service is the one telling you which menu goes in which store. Simple as that.&lt;/li&gt;
&lt;li&gt;Multiple different menu synchronizers: Taking into account the fact that each platform worked differently. Instead of conflating everything into a single service, we created one per integration, that was in charge of translating our menu representation into their representation, and allowing us to more finely manage errors, retrials and versioning.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's more complex, indeed, but ...&lt;/p&gt;

&lt;h2&gt;
  
  
  The conclusion
&lt;/h2&gt;

&lt;p&gt;What can we observe from all these examples?&lt;/p&gt;

&lt;p&gt;They all challenge a domain model and turn it into a more complex one. All of the examples seem to be an ode to complexity, but are they?&lt;/p&gt;

&lt;p&gt;What resulted, from all these wrongly simplified models, was a rigidity that forced developers to build and evolve around it, adding more complexity that the one that inherently comes from the domain.&lt;/p&gt;

&lt;p&gt;Inaccurate simplification causes rigidity. Rigidity causes workarounds. Workarounds cause complexity. The irony of it.&lt;/p&gt;

&lt;p&gt;The real world is littered with failed models—flat earth, geocentrism, luminiferous aether, caloric theory—each manageably simple yet catastrophically inaccurate. These frameworks clung to relevance only by grafting ever more exceptions onto their brittle cores, patching over mismatches between theory and reality.&lt;/p&gt;

&lt;p&gt;A good developer doesn't just write simple code. A good developer is aware that coding is an act of modeling domains, and that every different domain comes with an amount of complexity that cannot be avoided. A good developer has a sharp and keen eye to see and foresee this complexity, and builds software that accurately models it, consequently avoiding the extra complexity caused by misinterpreting reality.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Simplicity doesn’t come from ignoring complexity—it comes from accurately splitting it into, and rebuilding it from, manageable, composable parts."&lt;/em&gt;&lt;/p&gt;

</description>
      <category>simplicity</category>
      <category>softwaredesign</category>
      <category>ddd</category>
      <category>cleancode</category>
    </item>
    <item>
      <title>Dev Teams are not a democracy</title>
      <dc:creator>Alejandro Navas</dc:creator>
      <pubDate>Thu, 27 Mar 2025 16:12:11 +0000</pubDate>
      <link>https://dev.to/caeus/dev-teams-are-not-a-democracy-18mm</link>
      <guid>https://dev.to/caeus/dev-teams-are-not-a-democracy-18mm</guid>
      <description>&lt;p&gt;I’ve been interviewing for leadership-heavy roles lately. And while I do have experience leading, I’ve never really studied it—I just did what felt right at the time. Looking back, I know my choices benefited the team in the long run. But did I bulldoze them into doing my bidding?&lt;/p&gt;

&lt;p&gt;After some soul-searching, I realized something:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;I &lt;strong&gt;want&lt;/strong&gt; dev teams to be a democracy—because I want to be listened to.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;But I’ve &lt;strong&gt;led&lt;/strong&gt; like a tyrant—because I did what I thought was best, regardless of pushback.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s an awkward thing to recognize a bias. Why did I assume everyone should just get on board with my train of thought? Dev teams aren’t democracies. And good leaders don’t just listen; they also know when to ignore.&lt;/p&gt;

&lt;p&gt;So how do you strike the balance? How do you lead without oversteering?&lt;/p&gt;

&lt;p&gt;Three principles that worked for me:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Own your decisions—and their consequences.&lt;/strong&gt;&lt;br&gt;
As a leader, you will make calls that override others’ opinions. That’s inevitable. The key is to make &lt;strong&gt;informed bets&lt;/strong&gt; and take full responsibility when they don’t pan out. People respect a leader who owns their failures as much as their successes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Build trust first.&lt;/strong&gt;&lt;br&gt;
I started leading because people trusted my judgment. They didn’t push back—not because they were silent followers, but because I had a track record of &lt;strong&gt;making sharp decisions that made their lives easier&lt;/strong&gt;. Trust is built over time, and once you have it, leadership becomes less about authority and more about influence.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prototype ultra fucking fast.&lt;/strong&gt;&lt;br&gt;
Disagreements often stem from uncertainty. The faster you can turn an idea into something tangible, the faster you resolve debates. &lt;strong&gt;Working code dissolves arguments&lt;/strong&gt;. People don’t need to agree with you if they can see what works.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Follow up on concerns—personally.&lt;/strong&gt;&lt;br&gt;
Making a decision is easy; ensuring it succeeds is the real work. If you push for a change, you need to be the one &lt;strong&gt;helping the team adapt to it&lt;/strong&gt;. Whether that means teaching, troubleshooting, or proving its value, leadership isn’t just about saying "this is the way"—it’s about making the transition as smooth as possible.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At the end of the day, leadership isn’t about making everyone happy. It’s about making the right calls while keeping your team engaged, respected, and motivated. A little democracy, a little tyranny—just enough of both to keep things moving forward.&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>productivity</category>
      <category>teamwork</category>
      <category>techleadership</category>
    </item>
    <item>
      <title>Leadership without authority: How I stepped up without stepping over</title>
      <dc:creator>Alejandro Navas</dc:creator>
      <pubDate>Mon, 24 Mar 2025 12:44:05 +0000</pubDate>
      <link>https://dev.to/caeus/leadership-without-authority-how-i-stepped-up-without-stepping-over-4opi</link>
      <guid>https://dev.to/caeus/leadership-without-authority-how-i-stepped-up-without-stepping-over-4opi</guid>
      <description>&lt;p&gt;I once belonged to a team that lacked a more present leadership. It wasn’t that leadership was bad—quite the opposite. In fact, it was the best I’d ever had the opportunity to witness. But it was distant, absent from the trenches, detached from the technical struggles we faced daily.&lt;/p&gt;

&lt;p&gt;The team itself was made up of incredibly talented individuals. Yet, we weren’t working in tandem. Each person was pulling in a different direction, sometimes even inadvertently stepping on each other’s progress. There was no shared rhythm, no cohesion. We were like a band where every musician played their own tune, and somehow, we were supposed to create a symphony.&lt;/p&gt;

&lt;p&gt;That’s when I realized we needed something more—someone to glue things together, to harmonize the chaos. And so, I stepped up.&lt;/p&gt;

&lt;p&gt;Not because I was promoted. Not because someone told me to. But because it was necessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leading Without Authority
&lt;/h2&gt;

&lt;p&gt;I didn’t step in as a commander barking orders. That would have backfired instantly. Instead, I became the glue, the bridge between ideas, the quiet architect of a shared vision. I listened. I understood what each person was trying to accomplish and aligned their efforts so that we could move forward as a team, not as scattered individuals.&lt;/p&gt;

&lt;p&gt;I helped them deliver high quality, and I held myself to the same standard. Not by demanding it, but by demonstrating it. I gently nudged, suggested, and guided—never dictating, never micromanaging. I gave people the space to own their work, their successes, and their failures. And in doing so, I saw them grow, thrive, and collaborate in ways that hadn’t been happening before.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Leadership Philosophy I Still Follow
&lt;/h2&gt;

&lt;p&gt;That experience shaped how I see leadership to this day. It’s not about titles, authority, or hierarchy. It’s about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ownership&lt;/strong&gt; – Encouraging people to take responsibility for what they create.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aligned vision&lt;/strong&gt; – Ensuring that everyone moves toward a common goal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gentle steering&lt;/strong&gt; – Offering direction without force, guiding without stifling autonomy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leading by example&lt;/strong&gt; – Setting the standard through action, not words.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This kind of leadership isn’t loud. It doesn’t demand attention. But it creates impact. It fosters teams that are not only productive but also cohesive, motivated, and proud of what they build together.&lt;/p&gt;

&lt;p&gt;And the best part? Anyone can step into this role. You don’t need a title to lead. You just need to care enough to make a difference.&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>ownership</category>
      <category>teamwork</category>
      <category>collaboration</category>
    </item>
    <item>
      <title>My Quantum Regrets</title>
      <dc:creator>Alejandro Navas</dc:creator>
      <pubDate>Wed, 12 Mar 2025 11:29:52 +0000</pubDate>
      <link>https://dev.to/caeus/my-quantum-regrets-1o9f</link>
      <guid>https://dev.to/caeus/my-quantum-regrets-1o9f</guid>
      <description>&lt;p&gt;A few years ago, I decided to tattoo the results of the double-slit experiment on my arms: an interference pattern (bands of varying width) on my right arm and two distinct lines (no interference) on my left. At the time, it felt like a brilliant idea—a permanent tribute to one of the most fascinating experiments in quantum mechanics. But now, I regret it. Who could have guessed that something so meaningful at the time would lose its appeal? Yet, in a way, the tattoo is still deeply relevant to my life—not just as a scientific concept, but as a metaphor for the choices we make and the paths we take.&lt;/p&gt;

&lt;p&gt;The double-slit experiment, at its core, is about possibilities. When particles like electrons are not observed, they exist in a superposition of states, creating an interference pattern. But the moment we measure them, the superposition collapses, and they "choose" a single state. This idea of collapsing possibilities resonates with me, especially in the context of my career.&lt;/p&gt;

&lt;p&gt;Over the years, I’ve worked in various types of companies, and my experiences have been as dynamic as the interference pattern on my right arm. I’ve thrived in startups, where autonomy and proactive contributions are not only encouraged but celebrated. In those environments, I felt like I was in a state of superposition—full of possibilities, free to explore ideas, and empowered to make an impact. Startups, in my view, are like fertile ground for aligned, autonomous developers who proactively deliver results. They thrive on the energy of individuals who take ownership and push boundaries.&lt;/p&gt;

&lt;p&gt;On the other hand, consultancies and outsourced teams have been more like the two distinct lines on my left arm—rigid, predictable, and lacking the richness of interference. In those settings, I’ve withered. The lack of autonomy and rigid structures stifled my creativity and left me feeling unappreciated. It’s not that these environments are inherently bad—they just don’t align with how I work best. I’ve come to realize that I need the freedom to contribute my ideas and see them take shape, much like the interference pattern that emerges when possibilities are allowed to coexist.&lt;/p&gt;

&lt;p&gt;This brings me to my current struggle: finding a new job. In the past, I never had to look for more than a month. Now, I’ve been searching for a year with little success. Part of the problem is that I’m aiming higher—there are fewer opportunities that match my aspirations, and I haven’t fully adapted my profile to these higher-level positions. But another issue is my tendency to overthink, to linger in a state of superposition, paralyzed by the sheer number of possibilities. I need to learn to collapse—to commit to a direction, trust my instincts, and move forward.&lt;/p&gt;

&lt;p&gt;This is where the tattoo comes full circle. Just as the double-slit experiment shows us the importance of observation and choice, my career journey has taught me the importance of decisiveness. In interviews, for example, I often find myself branching into endless ideas, edge cases, and possibilities. But I’ve realized that this only leads to overwhelm. Instead, I need to collapse my superposition of thoughts, focus on one clear direction, and seek confirmation from the interviewer. It’s about choosing a path and committing to it, even if it means letting go of other possibilities.&lt;/p&gt;

&lt;p&gt;In the end, the tattoo is more than just a scientific curiosity—it’s a reminder to embrace the process of collapsing. Whether it’s in quantum mechanics, career choices, or everyday decisions, the act of choosing is what brings clarity and progress. And while I may regret the tattoo itself, the lesson it represents is one I’ll carry with me: sometimes, you have to collapse your possibilities to move forward.&lt;/p&gt;

</description>
      <category>careerdevelopment</category>
    </item>
    <item>
      <title>My DNA</title>
      <dc:creator>Alejandro Navas</dc:creator>
      <pubDate>Thu, 13 Feb 2025 15:33:13 +0000</pubDate>
      <link>https://dev.to/caeus/my-dna-38df</link>
      <guid>https://dev.to/caeus/my-dna-38df</guid>
      <description>&lt;p&gt;I take pride in my work. I’m a high-impact developer with a strong grasp of system design, architecture, and problem-solving. I bring ownership to everything I do, and my contributions consistently help teams move faster and build better software.&lt;/p&gt;

&lt;p&gt;That said, I recognize that technical skill alone isn't enough. I’ve struggled in interviews that require a strong personal connection, and my wife—bluntly but wisely—has pointed out that I sometimes come across as uncertain, even when I know my worth. I need to be more confident, more assertive—not just in what I can do, but in how I communicate it.&lt;/p&gt;

&lt;p&gt;To sharpen that, I’m writing a series of articles to keep my self-image clear and to answer questions about my skills and value without second-guessing myself. This first post is my version of a company’s “DNA” or “culture”—the principles that guide how I work and collaborate.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Empathy:&lt;/strong&gt; Software exists to make life easier—for users, developers, and our future selves. I care deeply about Developer Experience (DevX) and building solutions that are intuitive, reliable, and scalable. A well-designed system should be empowering, not frustrating, and I believe in writing code that continues to deliver value long after I’ve moved on.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Trust &amp;amp; Ownership:&lt;/strong&gt; I do my best work in environments built on trust and autonomy. When teams take real ownership of their work, they move with confidence rather than fear. I’ve seen how a lack of ownership leads to hesitation—changes become cautious, complexity creeps in, and developers start working around problems instead of fixing them. I want to help teams break that cycle by fostering a culture of accountability and trust.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alignment &amp;amp; Collaboration:&lt;/strong&gt; The best ideas come from different perspectives, and I value working toward shared goals with clear communication and mutual respect. I believe every team member brings something valuable to the table, and I make an active effort to align with others so we can build meaningful solutions together. Strong collaboration isn’t just about sharing ideas—it’s about making sure we’re all moving in the right direction.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Impactful Problem-Solving:&lt;/strong&gt; I don’t just focus on solving problems—I focus on solving the right problems. I aim for solutions that provide the most value with the least complexity, always keeping the bigger picture in mind. That said, I know that what seems “right” to me isn’t always obvious to others, and I make a conscious effort to listen, adapt, and challenge my own assumptions. Good problem-solving isn’t just about technical skill; it’s about understanding the broader context and making decisions that benefit the whole team.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Simple ain't easy</title>
      <dc:creator>Alejandro Navas</dc:creator>
      <pubDate>Wed, 16 Oct 2024 12:22:58 +0000</pubDate>
      <link>https://dev.to/caeus/simple-isnt-easy-22fa</link>
      <guid>https://dev.to/caeus/simple-isnt-easy-22fa</guid>
      <description>&lt;p&gt;&lt;strong&gt;"Keep It Simple, Stupid."&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;"Premature optimization is the root of all evil."&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;"Perfect is the enemy of good"&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;"Make it work, make it right, make it fast"&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;"You are not gonna need it" (YAGNI)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We've all heard these sayings and principles, right? So much so that they've been baked into our DNA as developers. We recite them like gospel, drop them in code reviews, and maybe even slap them onto a motivational poster with a picture of a sunrise.&lt;/p&gt;

&lt;p&gt;But let's take a step back. Have we misunderstood these principles? Are we taking them too literally, like some kind of techie version of 'telephone,' where by the time it reaches the last developer in the chain, Keep It Simple has morphed into Take shortcuts, do what's convenient for you, ignore edge cases, and move on?&lt;/p&gt;

&lt;h3&gt;
  
  
  Underengineering: The Silent Killer
&lt;/h3&gt;

&lt;p&gt;What if I told you that keeping things "simple" (or at least what sounds simple) can actually be more dangerous than adding that extra interface or helper method you were scolded for in the last sprint? Here’s why: simple code and easy code aren’t the same. And favoring "ease" tends to lead to underengineering. You know, that sneaky cousin of overengineering who always dodges the blame in post-mortems?&lt;/p&gt;

&lt;p&gt;Let’s get something clear—ease is subjective. "How easy is it to speak Chinese?" is a question with a hundred answers. If you’re from Beijing, it’s like taking a Sunday stroll. If you’re not, it’s like solving a Rubik’s Cube with your feet.&lt;/p&gt;

&lt;p&gt;But complexity is different. Complexity isn’t some vague feeling of frustration that arises when trying to understand code after a long night. It’s an objective measure of how many exceptions, irregularities, and "oh, I forgot about that!" cases exist within a system. The more special cases, the more complex the system—period.&lt;/p&gt;

&lt;p&gt;This distinction is crucial. When we write code that’s merely "easy," we're optimizing for ourselves and our current state of knowledge. The next developer, probably a junior bootcamp grad who's only been exposed to pristine textbook examples, is gonna look at your code and feel like a detective unravelling a conspiracy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Anecdote Time: The Non-escaped string.
&lt;/h3&gt;

&lt;p&gt;Picture this: A junior developer, fresh out of their bootcamp, is excited to tackle their first solo ticket. It's a small fix, a string field is not being correctly escaped in a certain endpoint. Being conscientious, they realize that it's a bug of the current implementation, one that could affect any endpoint that's processing strings with especial characters. "Should I fix the current implementation, and therefore fix not only this endpoint, but other that may be affected?"&lt;/p&gt;

&lt;p&gt;They ask the team, and the response is something like:&lt;br&gt;
"Don’t over-engineer it. Just fix that endpoint."&lt;/p&gt;

&lt;p&gt;And that’s how the codebase accumulates more of those one-off ad-hoc fixes, scattered all over like digital breadcrumbs. Next developer writing another endpoint may face the same problem, same bug, and will spend important time fixing something that could've been fixed if the team had chosen the simple solution over the easy one.&lt;/p&gt;

&lt;p&gt;As it stands, we've saved a few minutes up front and added an ongoing cost down the line —More time spent hunting, copy-pasting, and patching. We took the road of least resistance, mistaking it for the best path.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Burden of Knowledge: Write Code for stupid
&lt;/h3&gt;

&lt;p&gt;So, how do we avoid this pitfall? One trick is to imagine that you’re writing code to be maintained by a team of stupid fresh inexperienced junior developers. No, not that your fellow devs are stupid—although some codebases might make you wonder—but because it forces you to think in terms of clarity and coherence.&lt;/p&gt;

&lt;p&gt;Another helpful prompt: Would a junior developer be able to easily ruin this?&lt;/p&gt;

&lt;p&gt;If your code relies on deep domain knowledge or a trick you picked up in some obscure corner of the internet, there's a good chance it's not simple—no matter how "clean" it looks to you. Remember, what's intuitive for one developer may be a series of dark rituals for another. Code as if you're leaving behind a trail of breadcrumbs that any developer can follow, without needing to text you at 2 a.m. for directions.&lt;/p&gt;

&lt;h3&gt;
  
  
  KISS Isn't an Excuse for Laziness
&lt;/h3&gt;

&lt;p&gt;So, yes—Keep It Simple, Stupid. But also, Keep It Smart, Sensible. The problem with sayings is they don't tell you what to do when you've hit the sweet spot between too much and too little. When you take shortcuts, you're not simplifying, you're just putting future-you in a world of pain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion: Striking the Balance
&lt;/h3&gt;

&lt;p&gt;The real art lies in finding the balance—making code simple, not easy. Aim for solutions that are straightforward, not quick hacks that fit your immediate mental model. That might mean adding a couple more lines or splitting that big class that doesn't seem worth it now. Because good code is a lot like explaining something to a five-year-old: it takes extra effort to strip away the jargon, to rephrase the convoluted, and to ensure the core idea is both obvious and reusable.&lt;/p&gt;

&lt;p&gt;In the end, the difference between a complex mess and elegant simplicity is rarely about what you did, but why you did it. Take the time to do it right, and your code won't just be clean—it'll be future-proof. After all, the only thing worse than over-engineering is under-engineering.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Letter to my team: Fighting software decay</title>
      <dc:creator>Alejandro Navas</dc:creator>
      <pubDate>Wed, 09 Oct 2024 13:59:52 +0000</pubDate>
      <link>https://dev.to/caeus/letter-to-my-team-fighting-software-decay-pn0</link>
      <guid>https://dev.to/caeus/letter-to-my-team-fighting-software-decay-pn0</guid>
      <description>&lt;p&gt;Hey Team,&lt;/p&gt;

&lt;p&gt;I want to talk to you today about a hidden killer that lurks in every codebase: software entropy. It's that relentless force that slowly but surely makes our systems messier, more complex, and harder to work with over time. The impact? Longer development cycles, unexpected bugs, and dwindling team morale.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Software Entropy?
&lt;/h3&gt;

&lt;p&gt;Software entropy refers to the natural decay of software quality as changes accumulate. Think of it like a kitchen after a day of cooking — if no one cleans up after themselves, utensils pile up, dishes clutter, and eventually, the kitchen becomes unusable. Similarly, when developers rush to deliver features without considering the overall architecture or maintainability of the code, they contribute to the erosion of the codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  Examples of Entropy at Work
&lt;/h3&gt;

&lt;p&gt;Quick Fixes: Instead of addressing root causes, quick fixes are applied at the symptom level, creating a web of workarounds that are harder to unravel.&lt;/p&gt;

&lt;p&gt;Inconsistent Naming: Variable names that don’t reflect their true purpose lead to misunderstanding and misuse.&lt;/p&gt;

&lt;p&gt;Lack of Documentation: Missing or outdated documentation turns even simple tasks into time-consuming adventures.&lt;/p&gt;

&lt;p&gt;Ad-hoc Implementations: Code that works “just for now” inevitably becomes permanent, introducing unexpected side effects and making future changes riskier.&lt;/p&gt;

&lt;p&gt;When developers work in isolation, focused solely on delivering functionality, they often fall into these traps. It’s like cooking without cleaning up afterward — eventually, someone will be left with a mess that no one wants to deal with.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Experience Fighting Entropy
&lt;/h3&gt;

&lt;p&gt;I’ve been down this road before. In smaller teams, I was able to combat entropy by guiding the architecture and staying vigilant. But as teams grew and systems scaled, entropy had more places to hide. It became a game of whack-a-mole, with decay spreading wherever I couldn’t have my eyes on. The lesson I learned is that fighting entropy is a team effort.&lt;/p&gt;

&lt;p&gt;Without a shared commitment to maintaining code quality, decay wins. It’s inevitable. And while its impact on productivity may be subtle at first, it compounds over time, turning quick fixes into long-term roadblocks.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I’m Asking From You: DevX-Driven Development
&lt;/h3&gt;

&lt;p&gt;Now that you’re aware of this silent threat, I’m asking for your help to fight against it. How? By adopting what I call DevX-Driven Development.&lt;/p&gt;

&lt;p&gt;The core philosophy is simple: work to make your colleagues’ lives simpler. When you have to choose between making something easy for yourself or for your teammates, always choose your team. Think as if you're still not writing for the end user, but a library for your colleagues. If you can anticipate how your colleagues will experience your code — whether they’re maintaining it, extending it, or integrating with it — then you’ll know what quality truly means.&lt;/p&gt;

&lt;p&gt;Quality isn’t about ticking boxes on a checklist of best practices, following coding standards, or meeting test coverage targets. These are all surface-level manifestations of a deeper principle: empathy towards other developers.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Empathy Looks Like in Code
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Fix root causes instead of symptoms.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Choose clear and meaningful names.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Document your code, even if it seems obvious to you.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ration complexity in small, digestible doses.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Never a class too big, never a type too permissive, never a scope too polluted.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When we all act with empathy, decay will start to recede. Because decay thrives on quick fixes, and quick fixes are selfish acts. They focus on getting something working now rather than ensuring it will work well for others later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Boy Scout Rule: Leave the Code Cleaner Than You Found It
&lt;/h3&gt;

&lt;p&gt;That’s why I’m introducing one guiding rule for our fight against decay: the Boy Scout Rule. With every PR, no matter how small, leave the codebase a little better than it was before you touched it. Even if it’s just improving a comment, renaming a misleading variable, or extracting a small method, every step towards clarity is a step away from entropy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Closing Thoughts
&lt;/h3&gt;

&lt;p&gt;I don’t care which direction we take to achieve this, as long as we’re aligned in the fight against entropy. I’ll do my best to create alignment and provide a shared vision if one is lacking. But please, let’s ensure we have a clear north star, and let’s do a good deed with every PR we deliver.&lt;/p&gt;

&lt;p&gt;Together, we can keep entropy at bay and build something we’re all proud of.&lt;/p&gt;

</description>
      <category>cleancoding</category>
      <category>entropy</category>
      <category>softwaredevelopment</category>
      <category>teamwork</category>
    </item>
  </channel>
</rss>
