<?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: Rubén Rangel</title>
    <description>The latest articles on DEV Community by Rubén Rangel (@rubenerangel).</description>
    <link>https://dev.to/rubenerangel</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4211682%2F7bb30516-4bfd-46e9-8cd8-265cf013b2ea.jpg</url>
      <title>DEV Community: Rubén Rangel</title>
      <link>https://dev.to/rubenerangel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rubenerangel"/>
    <language>en</language>
    <item>
      <title>Stripe puts its AI agents in boxes. I did the same with my accounting system.</title>
      <dc:creator>Rubén Rangel</dc:creator>
      <pubDate>Tue, 27 Oct 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/rubenerangel/stripe-puts-its-ai-agents-in-boxes-i-did-the-same-with-my-accounting-system-iga</link>
      <guid>https://dev.to/rubenerangel/stripe-puts-its-ai-agents-in-boxes-i-did-the-same-with-my-accounting-system-iga</guid>
      <description>&lt;p&gt;&lt;em&gt;What caught my attention in Stripe's Minions, what I borrowed, what I left out on purpose, and how my own agents found the holes in the box I built for them.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;In February, Stripe published two articles (&lt;a href="https://stripe.dev/blog/minions-stripes-one-shot-end-to-end-coding-agents" rel="noopener noreferrer"&gt;part 1&lt;/a&gt; and &lt;a href="https://stripe.dev/blog/minions-stripes-one-shot-end-to-end-coding-agents-part-2" rel="noopener noreferrer"&gt;part 2&lt;/a&gt;) about &lt;strong&gt;Minions&lt;/strong&gt; , its internal coding agents. The number everyone repeated was the PR count: more than 1,300 a week merged into main, written start to finish by an agent, reviewed by people but without a single line of human code.&lt;/p&gt;

&lt;p&gt;The number didn't move me much. What stopped me was an idea from the second part: the system gets more reliable the more they &lt;strong&gt;lock the model into small boxes&lt;/strong&gt;. Reading it, I realized something uncomfortable about my own agent workflow: almost everything that protected it was a paragraph in a Markdown file.&lt;/p&gt;

&lt;p&gt;This article is about how I changed that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Stripe does, in five ideas
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Unattended, end to end.&lt;/strong&gt; An engineer tags the bot in a Slack thread and the next thing they see is a PR that passes CI, with no interaction in between. They do it because the company's scarcest resource is developer attention, and an unattended agent lets you launch several tasks in parallel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolation is what enables autonomy.&lt;/strong&gt; Each run happens on a &lt;em&gt;devbox&lt;/em&gt;, the same development machine humans use, ready in about 10 seconds and cut off from production and the internet. Since a mistake can only break that one devbox, the agent runs with full permissions and no confirmation prompts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blueprints.&lt;/strong&gt; The flow is a state machine written in code that mixes two kinds of node: deterministic ones (run linters, push), which never call an LLM, and agent ones (implement the task, fix CI), which do make decisions. Whatever can be anticipated is done by code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Curated context, not abundant context.&lt;/strong&gt; They have almost no global rules: rules load per subdirectory as the agent walks the repo. Their internal MCP server has close to 500 tools, and each agent gets only a small subset.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feedback shifted left, with a cap.&lt;/strong&gt; Anything that would fail in CI is caught earlier, by local linters that answer in under five seconds. And there are at most &lt;strong&gt;two rounds of CI&lt;/strong&gt; : after that, the PR goes back to a human.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Underneath it all is one idea that sums up their philosophy: what's good for humans is good for agents too. They didn't build new infrastructure for AI; the agents use the same tools their engineers have been tuning for years.&lt;/p&gt;

&lt;h2&gt;
  
  
  My context: one person, not a thousand
&lt;/h2&gt;

&lt;p&gt;I'm building &lt;strong&gt;PyME Contable&lt;/strong&gt; , a concept-based accounting system for small construction companies in Colombia, meant to grow into an ERP. Laravel 13, PostgreSQL 17 and a React SPA in TypeScript, all in Docker. I'm the only developer, working with an accountant who validates the accounting and tax rules.&lt;/p&gt;

&lt;p&gt;Some things in this system are never negotiable: a journal entry always balances, a posted document is never modified, a company never sees another company's data (RLS in Postgres), sequential numbers have no gaps, and an amount is never a &lt;code&gt;float&lt;/code&gt;. When two goals collide, the order is fixed: accounting integrity, security, compliance, performance, simplicity and, last, convenience.&lt;/p&gt;

&lt;p&gt;My agent workflow existed before I read Stripe. It's the evolution of the one I described in &lt;a href="https://rubenrangel.org/en/blog/four-ai-agents-build-my-code-the-most-valuable-one-knows-nothing" rel="noopener noreferrer"&gt;the previous article&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Architect&lt;/strong&gt; (the main session, Opus): plans, delegates and doesn't write code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tester&lt;/strong&gt; (Sonnet): writes one test and watches it fail before the code exists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coder&lt;/strong&gt; (Sonnet): writes the minimum to turn it green, without touching the test.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code, security and performance reviewers&lt;/strong&gt; (Opus): read, probe and report; they don't fix.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Me&lt;/strong&gt; : I approve every plan and every commit.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The hole Stripe showed me
&lt;/h2&gt;

&lt;p&gt;Comparing it to blueprints, I saw the problem. "The tester only writes in the tests folder", "the coder never touches tests", "reviewers don't write to the database": all of those were &lt;strong&gt;instructions&lt;/strong&gt;. Sentences in &lt;code&gt;.claude/agents/*.md&lt;/code&gt; that the model reads and, almost always, respects.&lt;/p&gt;

&lt;p&gt;Almost always is not a guarantee. A model can forget a rule in a long conversation, interpret it creatively or, worse, receive contrary instructions from a dependency's README or a web page. In a system whose first priority is accounting integrity, "almost always" isn't enough.&lt;/p&gt;

&lt;p&gt;Stripe doesn't trust the agent to behave: it trusts the box. That's what I wanted to borrow.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I didn't borrow, on purpose
&lt;/h2&gt;

&lt;p&gt;Before what I did borrow, what I didn't:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No unattended runs and no parallelism.&lt;/strong&gt; Stripe needs them because its bottleneck is the attention of thousands of engineers. Mine isn't volume, and my approvals aren't an obstacle: they're part of the control.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No custom harness.&lt;/strong&gt; Stripe forked an open-source agent. With a single developer, that's pure overengineering. I use Claude Code and its mechanisms: subagents, hooks and permissions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What transfers from Stripe are the patterns, not the parts.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I did borrow: deterministic gates
&lt;/h2&gt;

&lt;p&gt;I turned the most important rules into &lt;strong&gt;gates&lt;/strong&gt; : code that always runs and doesn't depend on the model remembering anything. I numbered them G0 to G5 so I could close them one by one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8xxhc1bhzl7r8g0gufmn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8xxhc1bhzl7r8g0gufmn.png" alt="My blueprint: deterministic nodes and agent nodes" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Gate&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Stripe idea&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;G0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One branch per step, PR into a protected &lt;code&gt;main&lt;/code&gt;, required CI and no admin bypass&lt;/td&gt;
&lt;td&gt;Push and CI as deterministic nodes&lt;/td&gt;
&lt;td&gt;Done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;G1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A hook that intercepts every tool call and allows or blocks it based on the agent's role&lt;/td&gt;
&lt;td&gt;Put the LLM in a box&lt;/td&gt;
&lt;td&gt;Done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;G2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fingerprint of approved tests: if the coder touches a test, it doesn't pass&lt;/td&gt;
&lt;td&gt;Code handles what can be anticipated&lt;/td&gt;
&lt;td&gt;Pending&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;G3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SQL mutations: deliberately break each Postgres invariant and require a test to fail&lt;/td&gt;
&lt;td&gt;Automated feedback to iterate against&lt;/td&gt;
&lt;td&gt;Pending&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;G4&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CI with actionlint, hadolint, &lt;code&gt;composer audit&lt;/code&gt; and &lt;code&gt;npm audit&lt;/code&gt;, which I also run locally before the PR&lt;/td&gt;
&lt;td&gt;Shift feedback left&lt;/td&gt;
&lt;td&gt;Done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;G5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Retire LLM review where a linter does better (CI, Dockerfile, lockfiles)&lt;/td&gt;
&lt;td&gt;An LLM only where judgment adds value&lt;/td&gt;
&lt;td&gt;Pending&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  G1: the box
&lt;/h3&gt;

&lt;p&gt;The core piece is a Claude Code &lt;code&gt;PreToolUse&lt;/code&gt; hook: a Python script that receives every attempt to use a tool, along with the type of agent making it, and answers yes or no. The rules, in short:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;tester&lt;/strong&gt; can only write to &lt;code&gt;backend/tests/**&lt;/code&gt; and to the frontend's &lt;code&gt;*.test.*&lt;/code&gt; files.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;coder&lt;/strong&gt; can't touch tests, &lt;code&gt;docs/&lt;/code&gt;, &lt;code&gt;.claude/&lt;/code&gt;, &lt;code&gt;CLAUDE.md&lt;/code&gt; or &lt;code&gt;.env&lt;/code&gt;, not even through Bash with &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;sed -i&lt;/code&gt; or &lt;code&gt;cp&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Neither of them runs git operations that write, migrations, or dependency installs.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;code reviewer&lt;/strong&gt; can only use &lt;code&gt;git diff&lt;/code&gt;, &lt;code&gt;status&lt;/code&gt;, &lt;code&gt;log&lt;/code&gt;, &lt;code&gt;show&lt;/code&gt; and read files.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;security and performance reviewers&lt;/strong&gt; can query Postgres, but as the application role, never as superuser, and any SQL that writes must run inside &lt;code&gt;BEGIN ... ROLLBACK&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Any other subagent is read-only.&lt;/li&gt;
&lt;li&gt;If the script crashes or Python is missing, it &lt;strong&gt;blocks&lt;/strong&gt;. It fails closed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is what a reviewer sees when it tries to delete a table outside a transaction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Blocked by .claude/hooks/role_guard.py: writing SQL must run inside
BEGIN ... ROLLBACK, never COMMIT. If you need it, stop and ask the architect.
exit=2

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same query wrapped in &lt;code&gt;BEGIN ... ROLLBACK&lt;/code&gt; goes through. The instruction in the Markdown is still there, but it's no longer the only thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  My agents reviewed the box, and broke it
&lt;/h2&gt;

&lt;p&gt;I ran the hook through my own workflow: the code reviewer and the security reviewer in parallel, on the uncommitted change. They found things nobody had seen: ways to slip past the rules with quoting and heredocs, a &lt;code&gt;psql&lt;/code&gt; meta-command left open, and a test for the hook itself that passed for the wrong reason, because the command it was supposed to block was already blocked by another rule. A test that can't fail proves nothing, which is exactly what this workflow exists to prevent.&lt;/p&gt;

&lt;p&gt;My rule, like Stripe's, is at most &lt;strong&gt;two rounds&lt;/strong&gt; of fixes. Here I ran a third one, as an explicit exception, because the findings crossed real boundaries.&lt;/p&gt;

&lt;p&gt;Then the architect pointed out something no reviewer had said: the subagents run in WSL &lt;strong&gt;as my user&lt;/strong&gt;. Any interpreter, a simple &lt;code&gt;python3 -c&lt;/code&gt; that writes a file, slips under the hook without it being able to read what it does. And there's something worse: the tester and the coder need &lt;code&gt;docker compose&lt;/code&gt; to run tests, and whoever can run Docker is root inside the containers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lesson: a guardrail is not a barrier
&lt;/h2&gt;

&lt;p&gt;That finding changed how I understand gate G1:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For the agents that &lt;strong&gt;write&lt;/strong&gt; (tester and coder), the hook is a &lt;strong&gt;guardrail&lt;/strong&gt; : it stops them when they make a mistake, which is what happens almost every time. It doesn't stop them if something induces them to evade it, and patching a blocklist for them has diminishing returns.&lt;/li&gt;
&lt;li&gt;For the &lt;strong&gt;reviewers&lt;/strong&gt; , it is a real &lt;strong&gt;barrier&lt;/strong&gt; , because there it works as an allowlist: whatever isn't explicitly authorized doesn't run.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The realistic threat isn't a malicious agent. It's a prompt injection arriving in a file or a page. Against that, the real barriers live outside the hook: a protected &lt;code&gt;main&lt;/code&gt; with no bypass, so my key can only push branches; an SSH key with a passphrase; and I do the pushes and merges myself.&lt;/p&gt;

&lt;p&gt;Here's the honest difference from Stripe. Their real box isn't the prompt or the hook: it's the devbox, a machine that can't see production, the internet, or anyone's credentials. I don't have devboxes. My box has holes, I know them, and they're written down in &lt;code&gt;decisiones.md&lt;/code&gt; as a trade-off, not hidden. The next step is evaluating Claude Code's Bash sandbox, which restricts reads, writes and network at the operating system level, to close the biggest ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Good for humans, good for agents", at a scale of one
&lt;/h2&gt;

&lt;p&gt;The idea that helped me most wasn't technical. I work alone and, even so, every step gets its own branch and goes in through a PR. It isn't bureaucracy: the PR is where CI runs and where the step's summary lives, with the reviewers' findings. It's the audit trail I'd want if I reviewed this system a year from now, and it's the same one an agent needs to understand what happened.&lt;/p&gt;

&lt;p&gt;And one rule I took from their "shift feedback left": &lt;strong&gt;whatever a reviewer finds once becomes a permanent test.&lt;/strong&gt; If the security reviewer spots a misconfigured &lt;code&gt;SECURITY DEFINER&lt;/code&gt; function, the fix doesn't end there: a test on the Postgres catalog stays behind and checks every function of that kind. That way, not repeating the mistake doesn't depend on another reviewer having the same good day.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Now I want to pull this workflow out of PyME Contable and use it in all my projects. I'm trying two ways of packaging it in parallel, a Claude Code plugin and a template, to compare the results. The rule for splitting is simple: roles, gates and the order of the flow are generic; the accounting invariants stay in the &lt;code&gt;CLAUDE.md&lt;/code&gt; of the repo that needs them.&lt;/p&gt;

&lt;p&gt;What I take from Stripe fits in one line: &lt;strong&gt;the instruction guides the agent, but what stops it is code.&lt;/strong&gt; And when code can't stop it, the honest thing is to write that down.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Sources: &lt;a href="https://stripe.dev/blog/minions-stripes-one-shot-end-to-end-coding-agents" rel="noopener noreferrer"&gt;Minions, part 1&lt;/a&gt; and &lt;a href="https://stripe.dev/blog/minions-stripes-one-shot-end-to-end-coding-agents-part-2" rel="noopener noreferrer"&gt;part 2&lt;/a&gt;, from Stripe's engineering blog. Tools: Claude Code with file-defined subagents, a Python &lt;code&gt;PreToolUse&lt;/code&gt; hook and GitHub Actions.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claudecode</category>
      <category>devops</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>From 354 MB to 16 MB: Docker under the hood, multi-stage builds and a Vercel deploy from the terminal</title>
      <dc:creator>Rubén Rangel</dc:creator>
      <pubDate>Sat, 03 Oct 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/rubenerangel/from-354-mb-to-16-mb-docker-under-the-hood-multi-stage-builds-and-a-vercel-deploy-from-the-1pd0</link>
      <guid>https://dev.to/rubenerangel/from-354-mb-to-16-mb-docker-under-the-hood-multi-stage-builds-and-a-vercel-deploy-from-the-1pd0</guid>
      <description>&lt;p&gt;&lt;em&gt;What an image really is, what a container is, why the order of your Dockerfile decides how long you wait on every build, and how I ended up shipping a Go server to Vercel with a single command.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;I wrote a twenty-line HTTP server in Go. The first image that packaged it weighed &lt;strong&gt;354 MB&lt;/strong&gt;. The final version, which does exactly the same thing, weighs &lt;strong&gt;16 MB&lt;/strong&gt;. I didn't change a single line of Go. I only changed how the Dockerfile was written.&lt;/p&gt;

&lt;p&gt;This article explains why, starting with the basics: what an image is and what a container is. If you already have those down, skip to the caching section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Image and container: the recipe and the dish
&lt;/h2&gt;

&lt;p&gt;An &lt;strong&gt;image&lt;/strong&gt; is a read-only package with everything an application needs to start: a minimal filesystem, libraries, the binary and the instruction for what to run. It doesn't run, doesn't change, has no state. It's a template.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;container&lt;/strong&gt; is a running image. Docker takes the image, puts a thin writable layer on top of it, gives it its own process and network space, and starts the command. Whatever the program writes goes to that layer, never to the image.&lt;/p&gt;

&lt;p&gt;If the image is the recipe, the container is the dish being served. You can serve ten dishes from the same recipe, and if you salt one of them, the recipe doesn't change and neither do the other nine.&lt;/p&gt;

&lt;h2&gt;
  
  
  How a Dockerfile becomes layers
&lt;/h2&gt;

&lt;p&gt;An image is not a single block. It's a &lt;strong&gt;stack of layers&lt;/strong&gt; , and almost every instruction in the Dockerfile produces one. Each layer stores only what changed compared to the previous one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj1f4k54kicvumdnmqf6u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj1f4k54kicvumdnmqf6u.png" alt="Each Dockerfile instruction becomes a layer of the image" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the final Dockerfile of my project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;golang:1.24-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /src&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go build &lt;span class="nt"&gt;-o&lt;/span&gt; /server main.go

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;alpine:3.20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;runner&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /server /server&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["/server"]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is what Docker reports for the final image with &lt;code&gt;docker history&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["/server"] 0B&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; /server /server 8.15MB&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["/bin/sh"] 0B&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; alpine-minirootfs-3.20.10 ... 7.81MB&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two layers with weight: the Alpine base system (7.8 MB) and my binary (8.1 MB). Instructions like &lt;code&gt;CMD&lt;/code&gt; or &lt;code&gt;WORKDIR&lt;/code&gt; only store metadata and weigh 0 bytes. There are the 16 MB, not one more.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cache: step order matters
&lt;/h2&gt;

&lt;p&gt;Because each layer depends on the one before it, Docker can reuse them. On a rebuild it walks the Dockerfile top to bottom and, as long as the instruction and its input files haven't changed, it uses the layer it already has stored. &lt;strong&gt;As soon as one layer changes, every layer after it gets rebuilt.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That turns ordering into a design decision. The rule: &lt;strong&gt;what rarely changes goes on top, what changes often goes at the bottom.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In a Go project with dependencies, the difference looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Bad: any code change invalidates the dependency download&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go mod download
&lt;span class="k"&gt;RUN &lt;/span&gt;go build &lt;span class="nt"&gt;-o&lt;/span&gt; /server .


&lt;span class="c"&gt;# Good: dependencies are only downloaded if go.mod or go.sum change&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; go.mod go.sum ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go mod download
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go build &lt;span class="nt"&gt;-o&lt;/span&gt; /server .

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the bad version, editing a comment in &lt;code&gt;main.go&lt;/code&gt; changes the context of &lt;code&gt;COPY . .&lt;/code&gt;, and that forces every dependency to be downloaded again. In the good one, &lt;code&gt;go.mod&lt;/code&gt; and &lt;code&gt;go.sum&lt;/code&gt; didn't change, so the dependency layer comes from cache and only the code is recompiled.&lt;/p&gt;

&lt;p&gt;The benefits are concrete:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Faster builds.&lt;/strong&gt; The expensive work (downloading dependencies, installing system packages) is done once and reused.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less network and disk.&lt;/strong&gt; Shared layers are stored only once, even if many images use them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lighter deploys.&lt;/strong&gt; When pushing or pulling an image, only the layers the destination doesn't have travel.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One detail that completes the rule: a &lt;code&gt;.dockerignore&lt;/code&gt; file that excludes &lt;code&gt;.git&lt;/code&gt;, &lt;code&gt;node_modules&lt;/code&gt; or local binaries keeps irrelevant changes from invalidating &lt;code&gt;COPY . .&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-stage: build in one image, run in another
&lt;/h2&gt;

&lt;p&gt;Here's the difference between 354 MB and 16 MB.&lt;/p&gt;

&lt;p&gt;To compile Go you need the compiler, the standard library and the build tools. To &lt;strong&gt;run&lt;/strong&gt; the resulting binary you need none of that. A single-stage Dockerfile puts everything into the final image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# The bad: a single stage. The final image drags the compiler along.&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; golang:1.24-alpine&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /src&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go build &lt;span class="nt"&gt;-o&lt;/span&gt; /server main.go
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["/server"]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;strong&gt;multi-stage build&lt;/strong&gt; separates responsibilities. Each &lt;code&gt;FROM&lt;/code&gt; starts a new stage with its own base:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;builder&lt;/code&gt;&lt;/strong&gt; starts from &lt;code&gt;golang:1.24-alpine&lt;/code&gt;, compiles and produces the binary. It's a throwaway stage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;runner&lt;/code&gt;&lt;/strong&gt; starts from &lt;code&gt;alpine:3.20&lt;/code&gt;, which is clean, and with &lt;code&gt;COPY --from=builder&lt;/code&gt; takes &lt;strong&gt;only&lt;/strong&gt; the binary.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Everything that was in &lt;code&gt;builder&lt;/code&gt; (the compiler, the source code, the build cache) stays out of the final image. Measured on my machine:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Image&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Contains the compiler?&lt;/th&gt;
&lt;th&gt;Contains the source code?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;builder&lt;/code&gt; stage (same as a single stage)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;354 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;runner&lt;/code&gt; stage (final image)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;16 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;95% smaller. And it's not just size: fewer things inside means fewer packages with vulnerabilities, nothing useful for an attacker who gets into the container, and faster startups because there's less to download.&lt;/p&gt;

&lt;p&gt;Naming the stages also helps with debugging. If something fails at compile time, you can build just that stage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile.vercel &lt;span class="nt"&gt;--target&lt;/span&gt; builder &lt;span class="nt"&gt;-t&lt;/span&gt; go-vercel:builder &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Many containers, one image, zero interference
&lt;/h2&gt;

&lt;p&gt;Because the image is read-only, nobody can modify it while it runs. Each container gets &lt;strong&gt;its own writable layer&lt;/strong&gt; on top of the same shared layers. That's what lets you start ten containers from the same image without them stepping on each other.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc424lmzrnyra0fqztrun.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc424lmzrnyra0fqztrun.png" alt="Several containers share the image layers and each one has its own writable layer" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I tested it with two containers from the same Alpine image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; a alpine:3.20 &lt;span class="nb"&gt;sleep &lt;/span&gt;300
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; b alpine:3.20 &lt;span class="nb"&gt;sleep &lt;/span&gt;300

docker &lt;span class="nb"&gt;exec &lt;/span&gt;a sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'echo hello &amp;gt; /note.txt'&lt;/span&gt;

docker &lt;span class="nb"&gt;exec &lt;/span&gt;a &lt;span class="nb"&gt;cat&lt;/span&gt; /note.txt &lt;span class="c"&gt;# hello&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;b &lt;span class="nb"&gt;cat&lt;/span&gt; /note.txt &lt;span class="c"&gt;# No such file or directory&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file exists in &lt;code&gt;a&lt;/code&gt; and not in &lt;code&gt;b&lt;/code&gt;. They wrote on top of the same image, but each one in its own layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why data is lost without a volume
&lt;/h2&gt;

&lt;p&gt;The writable layer lives and dies with the container. If you remove the container, its layer goes with it. Continuing the example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; a
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; a alpine:3.20 &lt;span class="nb"&gt;sleep &lt;/span&gt;300
docker &lt;span class="nb"&gt;exec &lt;/span&gt;a &lt;span class="nb"&gt;cat&lt;/span&gt; /note.txt &lt;span class="c"&gt;# No such file or directory&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same name, same image, but it's a new container with a new, empty layer. The note is gone.&lt;/p&gt;

&lt;p&gt;And that's not a bug, it's the design: containers are &lt;strong&gt;ephemeral&lt;/strong&gt; on purpose, so you can destroy, replace and scale them without fear. A version upgrade, for example, is exactly that: remove the old container and create a new one from the new image.&lt;/p&gt;

&lt;p&gt;Whatever has to survive (a database, files uploaded by users) goes in a &lt;strong&gt;volume&lt;/strong&gt; : storage that Docker manages outside the container and mounts at a path inside it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker volume create notes
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; notes:/data alpine:3.20 sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'echo hello &amp;gt; /data/note.txt'&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; notes:/data alpine:3.20 &lt;span class="nb"&gt;cat&lt;/span&gt; /data/note.txt &lt;span class="c"&gt;# hello&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first container wrote and was destroyed (&lt;code&gt;--rm&lt;/code&gt;). The second, brand new, found the file. The data lived in the volume, not in the container.&lt;/p&gt;

&lt;p&gt;The practical rule: &lt;strong&gt;the image is the code, the container is the process, the volume is the state.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The finale: from the terminal to Vercel
&lt;/h2&gt;

&lt;p&gt;With the image optimized, all that was left was shipping it. Vercel can run containers: if the project has a &lt;code&gt;Dockerfile.vercel&lt;/code&gt;, it uses it to build the image, starts the final stage and injects the &lt;code&gt;PORT&lt;/code&gt; variable. That's why the server reads it from the environment instead of hardcoding it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PORT"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"80"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The whole deploy was done from the terminal, with no connected repository and no pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vercel link # links the folder to a project
vercel --prod # builds the image and publishes it to production

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vercel detected the project with the &lt;strong&gt;Container&lt;/strong&gt; preset, built both stages, and 34 seconds later the server was responding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://05-vercel-bice.vercel.app
&lt;span class="c"&gt;# Hello from a container on Vercel 👋&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What I take away
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;An image is a read-only template made of layers; a container is that template running, with its own layer to write to.&lt;/li&gt;
&lt;li&gt;The order of the Dockerfile decides what gets reused from cache: stable things on top, changing things at the bottom.&lt;/li&gt;
&lt;li&gt;Multi-stage separates the workshop from the shop window: you build with everything, you ship only what's needed. In this case, from 354 MB to 16 MB.&lt;/li&gt;
&lt;li&gt;Containers from the same image don't affect each other because each one writes to its own layer.&lt;/li&gt;
&lt;li&gt;Whatever isn't in a volume is lost when the container is removed, and that's an advantage if you design for it.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;The code is at &lt;a href="https://github.com/rubenerangel/go-docker-vercel" rel="noopener noreferrer"&gt;github.com/rubenerangel/go-docker-vercel&lt;/a&gt; and the live demo at &lt;a href="https://05-vercel-bice.vercel.app" rel="noopener noreferrer"&gt;05-vercel-bice.vercel.app&lt;/a&gt;. Tools: Go 1.24, Docker with BuildKit, Alpine 3.20 and the Vercel CLI.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>go</category>
      <category>vercel</category>
    </item>
    <item>
      <title>Four AI agents build my code. The most valuable one knows nothing.</title>
      <dc:creator>Rubén Rangel</dc:creator>
      <pubDate>Tue, 15 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/rubenerangel/four-ai-agents-build-my-code-the-most-valuable-one-knows-nothing-4b4n</link>
      <guid>https://dev.to/rubenerangel/four-ai-agents-build-my-code-the-most-valuable-one-knows-nothing-4b4n</guid>
      <description>&lt;p&gt;&lt;em&gt;How I split the work between AI agents with locked-down roles, and why the last one — the one that arrives with no context — is the one that finds what everyone else misses.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;I recently finished a change to a KPI and payroll platform in eight steps. I verified every step myself, with my own eyes, before moving to the next. At the end, an agent I had told nothing about any of it found two problems I had completely missed.&lt;/p&gt;

&lt;p&gt;This article explains how I work with AI agents, who does what, and why that last agent is worth more precisely because it knows nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule that changes everything
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The AI builds; I direct.&lt;/strong&gt; I don't write the code: I write the instructions, hand out the work, and verify what comes back. It sounds like delegation, and it is. But it has a trap that took me a while to see: &lt;strong&gt;directing is not reviewing&lt;/strong&gt;. Whoever runs the build site knows too much about it to look at it with fresh eyes.&lt;/p&gt;

&lt;p&gt;To make the analogy work, picture a construction site with four people: an inspector, a bricklayer, a site foreman, and, at the very end, an outside surveyor. Each has a locked-down role and a list of things they are forbidden to do.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flykhrqse4ykyps3no7qo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flykhrqse4ykyps3no7qo.png" alt="The chain: inspector, bricklayer, foreman, and the surveyor outside the loop" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1 · The inspector writes the test before the wall exists
&lt;/h2&gt;

&lt;p&gt;In software, a &lt;em&gt;test&lt;/em&gt; is a small program that checks one single thing: "if this goes in, that must come out." The inspector writes &lt;strong&gt;one&lt;/strong&gt; test, for &lt;strong&gt;one&lt;/strong&gt; behavior, before the code that satisfies it exists. By definition, that test &lt;strong&gt;fails&lt;/strong&gt; (it's "red"). There is no wall yet.&lt;/p&gt;

&lt;p&gt;Its most important rule is easy to say and hard to follow: &lt;strong&gt;the correct answer is written by hand from the rule, never copied from what the program returns.&lt;/strong&gt; If the inspector runs the program and writes down whatever came out as "correct," the test will approve anything and can never contradict anyone.&lt;/p&gt;

&lt;p&gt;A real example. The work card said: "there are 15 browser tooltips to replace." That 15 came from the system's own counter. Counting by hand, there were &lt;strong&gt;17&lt;/strong&gt; : the counter had two holes and was missing two. Had we trusted the program, the job would have finished "green" with two tooltips still alive.&lt;/p&gt;

&lt;p&gt;Forbidden: writing solution code, touching any other test, and inventing business rules. If the expected value isn't on the card, it stops and asks.&lt;/p&gt;

&lt;h2&gt;
  
  
  2 · The bricklayer builds that wall, and only that one
&lt;/h2&gt;

&lt;p&gt;The bricklayer receives the red test and writes &lt;strong&gt;the minimum code&lt;/strong&gt; that turns it green. Nothing speculative, no "while I'm at it." Only the files it has been authorized to touch.&lt;/p&gt;

&lt;p&gt;It is forbidden to &lt;strong&gt;touch the test&lt;/strong&gt;. If it thinks the test is wrong, it says so and justifies it; it doesn't quietly rewrite it or mark it as "skipped." It doesn't fix other things along the way either, and it doesn't add anything nobody asked for.&lt;/p&gt;

&lt;p&gt;What I liked most about this role I saw when &lt;strong&gt;I was the one who got it wrong&lt;/strong&gt;. In one instruction I told it where to put a certain technical detail, and I was mistaken. The bricklayer neither obeyed nor changed it silently: &lt;strong&gt;it stopped, explained why my instruction was incorrect, and reported it.&lt;/strong&gt; I fixed the following instructions. An agent that pushes back with good reason is worth more than one that obeys.&lt;/p&gt;

&lt;h2&gt;
  
  
  3 · The foreman doesn't trust the report
&lt;/h2&gt;

&lt;p&gt;That's me. The foreman writes neither code nor tests: it hands out the assignments and &lt;strong&gt;verifies every delivery independently&lt;/strong&gt;. It's not enough for the inspector to say "the test fails": I run it myself and read why it fails. It's not enough for the bricklayer to say "it passes now": I run it myself.&lt;/p&gt;

&lt;p&gt;And there's one check I consider the most important of all: &lt;strong&gt;the test of the test.&lt;/strong&gt; Once everything is green, I remove only the bricklayer's fix and confirm the test goes red again. If it stays green without the fix, the test was useless and has to be redone. In this case, removing the fix turned the test red listing the 17 tooltips — not one more, not one less.&lt;/p&gt;

&lt;p&gt;The inspector → bricklayer → verification cycle repeated eight times, from small to large. On the last step, when running &lt;em&gt;all&lt;/em&gt; the platform's tests — not just the new one — nine old tests broke: they were looking up elements by the very attribute we were removing. The bricklayer &lt;strong&gt;stopped and reported it&lt;/strong&gt; instead of tweaking them to pass. That is exactly the behavior you want: a broken test is a question, not an obstacle.&lt;/p&gt;

&lt;p&gt;At the end, I counted browser tooltips on the running platform: &lt;strong&gt;186 before, 0 after.&lt;/strong&gt; Forty-six screenshots, light and dark. Everything verified.&lt;/p&gt;

&lt;p&gt;And still, things got past me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9815eh109bwar3fyuzl4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9815eh109bwar3fyuzl4.png" alt="What each role knows: the surveyor only receives the card and the final result" width="800" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4 · The outside surveyor: arrives knowing nothing
&lt;/h2&gt;

&lt;p&gt;The reviewer is an agent in a &lt;strong&gt;clean session&lt;/strong&gt; : it has no access to the conversation where everything was planned and built. It receives exactly two things: &lt;strong&gt;the original card&lt;/strong&gt; (what was asked) and &lt;strong&gt;the final result&lt;/strong&gt; (what changed). It is &lt;strong&gt;read-only&lt;/strong&gt; : it can't fix anything, only report.&lt;/p&gt;

&lt;p&gt;Its instructions begin like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You arrive without the context of the build and look at the
change as if it were someone else's. That ignorance is the
value: don't accept anything just because "it makes sense."

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It checks seven angles, always the same ones:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Did it do what the card asked, and only that?&lt;/strong&gt; Anything extra gets flagged.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Did it stay where it belonged?&lt;/strong&gt; Some areas of the system are off-limits. Touching one is a blocker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does the test actually work?&lt;/strong&gt; Would it fail without this fix? It says so explicitly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Did it invent business rules?&lt;/strong&gt; Thresholds, texts, or criteria the card didn't bring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does it hide any failure?&lt;/strong&gt; Errors swallowed in silence, defaults that paper over a real gap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does the interface follow the standards?&lt;/strong&gt; Components, accessibility, keyboard, contrast.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does every touched screen answer its seven states?&lt;/strong&gt; Empty, loading, error, no permission, one item, many items, very long text.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It reports from most serious to least, separating &lt;strong&gt;blocker&lt;/strong&gt; from &lt;strong&gt;suggestion&lt;/strong&gt;. And if it finds nothing in an angle, it says so: "I found nothing here" is information; silence is not.&lt;/p&gt;

&lt;h3&gt;
  
  
  What it found
&lt;/h3&gt;

&lt;p&gt;Eight findings. Two blockers. Both fixed.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Finding&lt;/th&gt;
&lt;th&gt;What was done&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Blocker.&lt;/strong&gt; There was a folder of browser tests — the ones that simulate a person using the platform — still looking up elements by the removed attribute. &lt;strong&gt;In eight steps nobody had opened it.&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Fixed and verified by actually running those tests, not by reasoning about them.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Blocker.&lt;/strong&gt; The written plan said twice "nothing becomes keyboard-focusable," and the code made three cells focusable.&lt;/td&gt;
&lt;td&gt;The code was right: the decision changed mid-way, with new information, and the plan wasn't updated. The plan was corrected.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"No screen-reader label is lost" was inaccurate: &lt;em&gt;descriptions&lt;/em&gt; are lost, not &lt;em&gt;names&lt;/em&gt;, in four places.&lt;/td&gt;
&lt;td&gt;Text corrected and declared in the delivery as a real loss.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;On the heaviest screen, tooltips had no width cap.&lt;/td&gt;
&lt;td&gt;Fixed and measured.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The plan required measuring performance on the screen with the most elements, and there was no measurement.&lt;/td&gt;
&lt;td&gt;Measured, three runs. Opening a week goes from 152 ms to 265 ms; nothing else moves.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The counter still has three theoretical holes.&lt;/td&gt;
&lt;td&gt;Declared, not fixed: none exists in the system today. Closing them is another card.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;None of the findings was resolved by touching a test to make it pass. None was dismissed without a reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I can't be the reviewer
&lt;/h2&gt;

&lt;p&gt;Look at the first blocker. I verified eight steps, ran every test twice, removed the fix to see it fail again, took 46 screenshots. And I never opened that folder. Not because I was careless, but because &lt;strong&gt;I knew where to look, and that's why I didn't look where I didn't know.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's the problem with context. Whoever planned and directed a build reviews with a map in their head: they check the places the map marks. The surveyor has no map. It looks at the whole result, with the same seven questions as always, and finds what the map didn't include.&lt;/p&gt;

&lt;p&gt;The second blocker is the same lesson from the other side: the plan and the code contradicted each other, and I didn't see it &lt;strong&gt;because I knew why&lt;/strong&gt;. The decision had changed mid-way, for good reason, and in my head the contradiction didn't exist. For someone who only reads the paper and the result, it did.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://rubenrangel.org/en/blog/my-blog-posts-to-linkedin-automatically-but-not-without-me" rel="noopener noreferrer"&gt;the previous article&lt;/a&gt; I wrote: &lt;em&gt;automate the proposal, never the approval&lt;/em&gt;. This is the other half: &lt;strong&gt;whoever approves cannot be whoever built&lt;/strong&gt; , not even when the builder is the AI and the director is you. A reviewer with no context is not a luxury. It's the only way anyone looks with fresh eyes.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tools: Claude Code with file-defined agents (one per role, with restricted tools: the reviewer can only read). The method comes from a decades-old practice — the failing test first — applied to agents instead of people.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>softwareengineering</category>
      <category>testing</category>
      <category>productivity</category>
    </item>
    <item>
      <title>What to Prepare for a Tech Interview</title>
      <dc:creator>Rubén Rangel</dc:creator>
      <pubDate>Sun, 03 Aug 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/rubenerangel/what-to-prepare-for-a-tech-interview-5hk8</link>
      <guid>https://dev.to/rubenerangel/what-to-prepare-for-a-tech-interview-5hk8</guid>
      <description>&lt;p&gt;If you're getting ready for a technical interview, it's important to understand that just knowing how to code is no longer enough.&lt;/p&gt;

&lt;p&gt;Below, I share the key areas you need to master, based on my experience working with companies like &lt;strong&gt;SproutLoud&lt;/strong&gt; , &lt;strong&gt;Ansira&lt;/strong&gt; , &lt;strong&gt;Imaginamos&lt;/strong&gt; , and more.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ 1. Have a short &lt;em&gt;elevator pitch&lt;/em&gt; ready (45–60 seconds)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"I'm Rubén Rangel, a Full-Stack developer with 18 years of experience, mainly strong in PHP, JavaScript, Vue.js, Angular, Laravel, and NestJS. I enjoy building systems from scratch and I'm passionate about clean code, system design, and performance. I've led the full architecture of projects like Indigos Network, and I currently contribute to platforms at SproutLoud and Ansira that integrate marketing and automation at scale."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;📌 &lt;em&gt;Don't go on too long—they will ask you more questions later.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ 2. Prepare for questions about specific technologies
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Can you tell me about your experience working with PHP, Python, JavaScript (React, Angular, or Vue)? What’s your strongest skill?&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I have over 18 years of experience with JavaScript, including frameworks like Vue.js and Angular.&lt;br&gt;&lt;br&gt;
Vue.js is my strongest area, followed by Angular (up to version 9).&lt;br&gt;&lt;br&gt;
I’ve used Vue.js extensively for dashboards and dynamic interfaces, and Angular for complex enterprise apps.&lt;/p&gt;

&lt;p&gt;I’ve also worked with Python for web scraping, backend services (PHP), REST APIs, and automation tasks.&lt;/p&gt;

&lt;p&gt;I don’t have production experience with C#.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Always be honest.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  ✅ 3. Clearly explain your experience in software architecture
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Have you designed and led the technical architecture of a software product? Can you give an example?&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Yes. At Indigos Network, I was responsible for designing and implementing the entire architecture of a purchasing platform that connects clients with suppliers. It included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Modular backend in Laravel with domain-based structure&lt;/li&gt;
&lt;li&gt;Frontend in Angular 9&lt;/li&gt;
&lt;li&gt;Real-time notifications using Firebase&lt;/li&gt;
&lt;li&gt;Infrastructure deployed on AWS (EC2, S3)&lt;/li&gt;
&lt;li&gt;RESTful API and authentication layers&lt;/li&gt;
&lt;li&gt;CI/CD automation and cloud deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I led decisions on the database schema, system scalability, and infrastructure setup at companies like Ormigga SAS, Tres Astronautas, Imaginamos, and Ansira.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Prepare 1 or 2 real architecture examples you've designed
&lt;/h3&gt;

&lt;p&gt;Focus on:&lt;/p&gt;




&lt;h3&gt;
  
  
  🟨 Example 1: B2B Purchasing Platform for Companies (Ormigga)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Technology used:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; PHP with Laravel 5.6 (later upgraded to 7.x)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; Angular 6 (upgraded to 9)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database:&lt;/strong&gt; MySQL&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Realtime/Notifications:&lt;/strong&gt; Firebase&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud:&lt;/strong&gt; AWS (EC2, S3, Route 53)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;System scalability:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To support user and transaction growth, I designed the architecture using AWS services. We used EC2 with autoscaling to handle traffic spikes, S3 for file storage, and Route 53 for load balancing and DNS. I modularized the backend into separate services (auth, purchases, suppliers), which allowed for horizontal scaling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key decisions I made:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Led the migration from Laravel 5.6 to 7.x, improving performance and compatibility&lt;/li&gt;
&lt;li&gt;Upgraded the frontend from Angular 6 to 9 to take advantage of Ivy and faster load times&lt;/li&gt;
&lt;li&gt;Implemented granular roles and permissions in the backend to control purchase approval flows&lt;/li&gt;
&lt;li&gt;Used Firebase to implement a real-time notification system and chat between client and supplier&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Problem it solved:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The company needed to digitize and scale its corporate purchasing process. Everything was manual before. The system enabled automation of the relationship between suppliers and buyers, budget control, cost optimization, and better traceability.&lt;/p&gt;




&lt;p&gt;The platform needed to process and deliver personalized marketing campaigns to thousands of brand partners across multiple channels. My work helped make this logic more robust and scalable, ensuring performance and reliability for high-volume and high-priority campaigns. --&amp;gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ 4. Frequently Asked Interview Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🧠 How many years of experience do you have in digital transformation?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;I have over 18 years of experience in digital transformation, especially in SaaS products.&lt;br&gt;&lt;br&gt;
For example, at Indigos Network, Imaginamos, SproutLoud, and Ansira, I led projects that replaced manual processes with complete web systems (dashboards, workflows, reports, APIs).&lt;br&gt;&lt;br&gt;
These efforts helped reduce human errors, save operational time, and centralize business data.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  💬 Have you worked directly with clients in consulting or custom development environments?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Yes, I’ve worked directly with clients in multiple custom development agencies, including Indigos Network, Videobase, Imaginamos, and Tres Astronautas.&lt;br&gt;&lt;br&gt;
I always aim to translate technical terms into business value: I explain decisions based on benefits, use diagrams, and validate their needs with prototypes or user stories before developing.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  💰 What is your salary expectation in USD?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;I’m open to discussing fair compensation based on the role's responsibilities.&lt;br&gt;&lt;br&gt;
Based on my experience and the market, my target range is between &lt;strong&gt;$XXXXX&lt;/strong&gt; and &lt;strong&gt;$XXXXX&lt;/strong&gt; USD per year.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;🇬🇧 &lt;strong&gt;English Level&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
📖✍️ Fluent in reading and writing (C1).&lt;br&gt;&lt;br&gt;
🧑‍💼💬 Comfortable handling meetings and asynchronous communication.&lt;br&gt;&lt;br&gt;
🗣️ Currently improving my spoken English (B2) and progressing quickly towards full fluency (C1).&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Key Technologies
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Languages:&lt;/strong&gt; JavaScript, TypeScript, PHP, Python, SQL&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Frameworks:&lt;/strong&gt; Vue.js, Angular, NestJS, Laravel, Django&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Node.js, REST APIs, GraphQL&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Databases:&lt;/strong&gt; PostgreSQL, MySQL, MongoDB, DynamoDB&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Cloud &amp;amp; DevOps:&lt;/strong&gt; AWS (EC2, S3, RDS, Lambda, Fargate), Firebase, Docker, Redis&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Others:&lt;/strong&gt; TDD, SOLID, DDD, OAuth2, Git, Linux, WordPress&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Soft skills:&lt;/strong&gt; Leadership, Agile/Scrum, Client Communication, Code Review&lt;/p&gt;




&lt;h2&gt;
  
  
  🧭 Know Yourself
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Strengths and Weaknesses
&lt;/h3&gt;

&lt;p&gt;✅ I have over 18 years of experience with &lt;strong&gt;PHP&lt;/strong&gt; and &lt;strong&gt;JavaScript&lt;/strong&gt; , which allows me to confidently handle complex projects.&lt;br&gt;&lt;br&gt;
💬 I know how to gather requirements directly from clients and translate them into effective technical solutions.&lt;br&gt;&lt;br&gt;
🧠 When I need to work with technologies I'm not familiar with, I use &lt;strong&gt;Artificial Intelligence&lt;/strong&gt; to find accurate and efficient paths to meet the requirements without wasting time.&lt;br&gt;&lt;br&gt;
⚙️ I haven't worked with &lt;strong&gt;Flask&lt;/strong&gt; or &lt;strong&gt;React&lt;/strong&gt; in production, but I have solid experience with &lt;strong&gt;NestJS&lt;/strong&gt; and &lt;strong&gt;Vue.js&lt;/strong&gt; , and I learn new frameworks quickly when needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Smart Questions for the End of the Interview
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;How is the engineering team structured in this role?&lt;/li&gt;
&lt;li&gt;What challenges is the product currently facing?&lt;/li&gt;
&lt;li&gt;What technologies do you plan to adopt or evolve over the next year?&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧱 Scalable Architecture Design (Frontend + Backend + Data + AI)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Frontend
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;SPA built with React or Vue 3&lt;/li&gt;
&lt;li&gt;Tailwind CSS to keep the code clean and maintainable&lt;/li&gt;
&lt;li&gt;Internationalized content (i18n)&lt;/li&gt;
&lt;li&gt;SEO optimized with prerendering or SSR (Next.js)&lt;/li&gt;
&lt;li&gt;Integration with a secure and managed API Gateway&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Backend
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Microservices using Laravel (PHP) or Spring Boot&lt;/li&gt;
&lt;li&gt;Communication via events or queues (Kafka or RabbitMQ)&lt;/li&gt;
&lt;li&gt;Orchestration in Kubernetes (EKS or GKE)&lt;/li&gt;
&lt;li&gt;Authentication: OAuth2 with Laravel Passport or Auth0&lt;/li&gt;
&lt;li&gt;CI/CD: GitLab Pipelines with testing, security, and automatic deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Data
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Transactional database: PostgreSQL with replication and partitioning&lt;/li&gt;
&lt;li&gt;Data lake in AWS S3 to store events, logs, and training datasets&lt;/li&gt;
&lt;li&gt;Streaming and ETL: Kafka + Apache Flink&lt;/li&gt;
&lt;li&gt;Dashboarding: Metabase or Apache Superset&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. AI / ML
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Python (FastAPI) to expose trained models as microservices&lt;/li&gt;
&lt;li&gt;Training with scikit-learn or TensorFlow, using pipelines in AWS SageMaker or Vertex AI&lt;/li&gt;
&lt;li&gt;Anonymized and versioned data using DVC or MLflow&lt;/li&gt;
&lt;li&gt;Models acting as internal services or asynchronously via queues&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🚀 Scalability and Observability
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;📈 &lt;strong&gt;Horizontal autoscaling per service&lt;/strong&gt; with Kubernetes + HPA&lt;/li&gt;
&lt;li&gt;📊 &lt;strong&gt;Observability&lt;/strong&gt; with Prometheus + Grafana&lt;/li&gt;
&lt;li&gt;🕵️‍♂️ &lt;strong&gt;Tracing&lt;/strong&gt; with Jaeger&lt;/li&gt;
&lt;li&gt;📄 &lt;strong&gt;Logs&lt;/strong&gt; with the ELK Stack&lt;/li&gt;
&lt;li&gt;🧩 &lt;strong&gt;Event-driven architecture&lt;/strong&gt; to decouple services&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧠 Key Decisions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ Choose &lt;strong&gt;Laravel&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🧠 Separate &lt;strong&gt;AI&lt;/strong&gt; into its own domain to isolate complexity&lt;/li&gt;
&lt;li&gt;📬 Use &lt;strong&gt;queues and cache (Redis)&lt;/strong&gt; to decouple expensive processes&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔍 Differences Between Monolith, Microservices, and Event-Driven Architecture
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;🧱 Monolith&lt;/th&gt;
&lt;th&gt;🔗 Microservices&lt;/th&gt;
&lt;th&gt;📡 Event-Driven Architecture&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Definition&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Entire system in one app&lt;/td&gt;
&lt;td&gt;Independent services&lt;/td&gt;
&lt;td&gt;Communication through events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deployment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single unit&lt;/td&gt;
&lt;td&gt;Each service is separate&lt;/td&gt;
&lt;td&gt;Services react to events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scalability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Scales everything together&lt;/td&gt;
&lt;td&gt;Each service scales independently&lt;/td&gt;
&lt;td&gt;Scales based on event flows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Maintenance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Harder as it grows&lt;/td&gt;
&lt;td&gt;More maintainable and modular&lt;/td&gt;
&lt;td&gt;Modular and decoupled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Internal communication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Functions or classes&lt;/td&gt;
&lt;td&gt;REST APIs or gRPC&lt;/td&gt;
&lt;td&gt;Messages/events (Kafka, RabbitMQ, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Typical example&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;All-in-one Laravel app&lt;/td&gt;
&lt;td&gt;Auth, Orders, Payments, etc.&lt;/td&gt;
&lt;td&gt;User registers → emits event → others react&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ☁️ Using AWS in Scalable Architectures
&lt;/h2&gt;

&lt;p&gt;I have worked directly with key AWS services to build and scale robust applications in production:&lt;/p&gt;

&lt;h3&gt;
  
  
  1️⃣ EC2 (Elastic Compute Cloud)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🚀 Host backend servers in production and staging&lt;/li&gt;
&lt;li&gt;⚙️ Set up autoscaling + load balancers (ELB)&lt;/li&gt;
&lt;li&gt;🔁 Automate deployments with CI/CD&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2️⃣ Lambda
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🖼️ Image processing when uploading to S3&lt;/li&gt;
&lt;li&gt;🔔 Send asynchronous notifications&lt;/li&gt;
&lt;li&gt;✅ Run validations on specific forms&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3️⃣ S3 (Simple Storage Service)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;📁 Store attachments, media content, and backups&lt;/li&gt;
&lt;li&gt;🔐 Configure secure access policies and enable versioning&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4️⃣ RDS (Relational Database Service)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;💾 I use MySQL and PostgreSQL&lt;/li&gt;
&lt;li&gt;📊 Set up read replicas and automatic backups&lt;/li&gt;
&lt;li&gt;🌐 High availability with multi-AZ instances&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5️⃣ API Gateway
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🔒 Expose secure endpoints to microservices or Lambdas&lt;/li&gt;
&lt;li&gt;⚠️ Set up throttling and authentication (JWT / Cognito)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6️⃣ Other key components
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;📡 &lt;strong&gt;CloudWatch:&lt;/strong&gt; Logs and custom alerts&lt;/li&gt;
&lt;li&gt;🌍 &lt;strong&gt;Route 53:&lt;/strong&gt; DNS and health checks&lt;/li&gt;
&lt;li&gt;🔐 &lt;strong&gt;IAM:&lt;/strong&gt; Minimum necessary permissions&lt;/li&gt;
&lt;li&gt;📬 &lt;strong&gt;SQS:&lt;/strong&gt; Queues to decouple heavy processes (reports, emails)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔐 How to protect sensitive information in distributed systems
&lt;/h2&gt;

&lt;p&gt;In distributed systems involving multiple services, environments, and users, protecting sensitive information is a top priority. These are the strategies I apply:&lt;/p&gt;




&lt;h3&gt;
  
  
  1️⃣ Encryption
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;🔒 &lt;strong&gt;In transit:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
I use &lt;strong&gt;HTTPS/TLS&lt;/strong&gt; for all communication between services, frontend, and APIs.&lt;br&gt;&lt;br&gt;
Example: API Gateway + ACM certificates in AWS.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🗄️ &lt;strong&gt;At rest:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
I enable &lt;strong&gt;AES-256 encryption&lt;/strong&gt; in databases (RDS), storage (S3), and backups.&lt;br&gt;&lt;br&gt;
I also encrypt sensitive data in the app before storing it (e.g., tokens or card numbers).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2️⃣ Secret management
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🧪 I use &lt;strong&gt;AWS Secrets Manager&lt;/strong&gt; or &lt;strong&gt;Parameter Store&lt;/strong&gt; to manage keys, passwords, tokens, and credentials.&lt;/li&gt;
&lt;li&gt;❌ I avoid exposing sensitive data in environment variables or code.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3️⃣ Access control
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🛡️ I apply the &lt;strong&gt;principle of least privilege&lt;/strong&gt; using IAM for services and users.&lt;/li&gt;
&lt;li&gt;🔐 I use &lt;strong&gt;OAuth2 / JWT&lt;/strong&gt; to authorize access between systems and define scopes.&lt;/li&gt;
&lt;li&gt;🧩 &lt;strong&gt;I separate environments&lt;/strong&gt; (dev, staging, prod) with distinct roles and policies.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4️⃣ Traceability and auditing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;📋 I log access and actions in &lt;strong&gt;CloudWatch&lt;/strong&gt; , log systems (ELK stack), or tools like &lt;strong&gt;Datadog&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;🔍 I audit changes to sensitive data (users, finances, permissions).&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  5️⃣ Validation and sanitization
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⚠️ I apply &lt;strong&gt;strict validation&lt;/strong&gt; and sanitization to prevent attacks (SQL injection, XSS).&lt;/li&gt;
&lt;li&gt;🔁 I validate both on the frontend and in the APIs.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  6️⃣ Incident response
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🚨 I set up alerts for unusual access or security errors.&lt;/li&gt;
&lt;li&gt;🔁 I have a &lt;strong&gt;key rotation and recovery plan&lt;/strong&gt; in case of exposure.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🛡️ Security in modern systems: OAuth2, access roles, and encryption
&lt;/h2&gt;




&lt;h3&gt;
  
  
  🔑 OAuth2
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;In projects like &lt;strong&gt;Ormigga&lt;/strong&gt; , I used &lt;strong&gt;Laravel Passport&lt;/strong&gt; to implement OAuth2.&lt;/li&gt;
&lt;li&gt;I issue &lt;strong&gt;access tokens&lt;/strong&gt; with defined scopes.&lt;/li&gt;
&lt;li&gt;I use &lt;strong&gt;JWT&lt;/strong&gt; as the token format to validate permissions without querying the database.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  👥 Access roles
&lt;/h3&gt;

&lt;p&gt;👥 Access roles&lt;br&gt;&lt;br&gt;
🔐 I design authentication systems with hierarchical roles like &lt;strong&gt;Admin&lt;/strong&gt; , &lt;strong&gt;Editor&lt;/strong&gt; , and &lt;strong&gt;User&lt;/strong&gt; , adapting them to business needs.&lt;br&gt;&lt;br&gt;
⚖️ I make sure each role only has access to what's necessary, following best practices for security and user experience.&lt;br&gt;&lt;br&gt;
🛠️ I’ve implemented these role schemes both in systems built from scratch and in existing platforms.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛡️ Secure design best practices (OWASP principles)
&lt;/h2&gt;

&lt;p&gt;In every system I design or maintain, I apply OWASP project principles to mitigate common risks and ensure security from the architecture level.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ 1. Strict input validation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;I never trust data coming from the client&lt;/li&gt;
&lt;li&gt;I use server-side validation (&lt;code&gt;Laravel Requests&lt;/code&gt;) and sanitization to avoid injections (SQL, XSS)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔐 2. Authentication and session management
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;I implement strong authentication (OAuth2, JWT)&lt;/li&gt;
&lt;li&gt;I use token expiration, revocation, and inactive session detection&lt;/li&gt;
&lt;li&gt;I protect against brute-force attacks by limiting login attempts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔒 3. Role-based authorization
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;I use RBAC with detailed permission control&lt;/li&gt;
&lt;li&gt;Each endpoint checks the user's role before allowing the action (principle of least privilege)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔏 4. Strong encryption
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Encryption in transit (TLS 1.2/1.3) for all communication&lt;/li&gt;
&lt;li&gt;Encryption at rest in databases and storage (AES-256)&lt;/li&gt;
&lt;li&gt;Passwords are stored using &lt;code&gt;bcrypt&lt;/code&gt; or &lt;code&gt;argon2&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🚫 5. Secure error handling
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;I don’t expose traces, internal details, or sensitive messages to users&lt;/li&gt;
&lt;li&gt;Errors are logged internally for auditing without compromising security&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔐 6. Secure defaults
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;All features start from a secure posture: access is denied unless explicitly allowed&lt;/li&gt;
&lt;li&gt;I disable unused endpoints or configurations (e.g., debug in production)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🛑 7. CSRF and XSS protection
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;In Laravel, I enable CSRF middleware for forms&lt;/li&gt;
&lt;li&gt;I use &lt;code&gt;Content-Security-Policy&lt;/code&gt; and properly escape dynamic data in the frontend&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🤖 Use of AI / LLMs in modern systems
&lt;/h2&gt;

&lt;p&gt;I’ve started integrating Artificial Intelligence and large language models (LLMs) into web systems to automate tasks, enhance user experience, and offer advanced features.&lt;/p&gt;

&lt;h3&gt;
  
  
  💡 Use cases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;📝 Automated content generation (descriptions, support replies, email drafts)&lt;/li&gt;
&lt;li&gt;💬 Internal assistants for clients or providers (e.g., guided chatbots)&lt;/li&gt;
&lt;li&gt;🏷️ Information classification (automatic tags, intent detection)&lt;/li&gt;
&lt;li&gt;🎯 Personalized recommendations based on user behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🛠️ Technologies used
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OpenAI API (GPT-4):&lt;/strong&gt; for natural language tasks, integrated via backend in Laravel or Python microservice (FastAPI)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hugging Face Transformers:&lt;/strong&gt; for local inference in controlled environments where confidentiality is required&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python (scikit-learn, pandas, spaCy):&lt;/strong&gt; for lightweight NLP or classification models&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS S3 and RDS:&lt;/strong&gt; to store training and inference datasets&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🧩 Integration into architecture
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Models are exposed as REST microservices or integrated via queues (SQS/Kafka) for asynchronous processes&lt;/li&gt;
&lt;li&gt;For sensitive tasks, I anonymize and clean data before sending it to external services&lt;/li&gt;
&lt;li&gt;I use logs and metrics to evaluate response quality and detect errors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8hykel34yxqjwynkqd00.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8hykel34yxqjwynkqd00.png" alt="AI integration into architecture" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🌍 Experience working with clients on real-world projects
&lt;/h2&gt;

&lt;p&gt;Throughout my career, I’ve worked with various clients in countries like 🇻🇪 Venezuela, 🇨🇴 Colombia, 🇪🇸 Spain, and 🇨🇱 Chile, in companies such as &lt;strong&gt;Gaviria Software&lt;/strong&gt; , &lt;strong&gt;Videobase&lt;/strong&gt; , &lt;strong&gt;Indigos Network&lt;/strong&gt; , &lt;strong&gt;Tres Astronautas&lt;/strong&gt; , and &lt;strong&gt;Ormigga SAS&lt;/strong&gt; , participating in the development of custom, scalable technology solutions aligned with real business goals.&lt;/p&gt;

&lt;h3&gt;
  
  
  🚀 Notable examples:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;📺 Videobase:&lt;/strong&gt; Developed custom streaming platforms, optimizing video upload and delivery using AWS S3 and CloudFront&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🏢 Ormigga SAS:&lt;/strong&gt; B2B purchasing platform with budget control, approval workflows, client-provider chat, and cloud storage (EC2, S3, RDS)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📣 Indigos Network:&lt;/strong&gt; Marketing campaign automation for multiple brands, integrating third-party APIs and event-driven workflows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;💼 Gaviria Software:&lt;/strong&gt; Robust backend development with Laravel and PostgreSQL for financial control and licensing systems&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;👨‍🚀 Tres Astronautas:&lt;/strong&gt; Frontend projects with Angular and backend with NodeJS/NestJS for SaaS products&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In all these projects, I not only participated as a developer but also made architectural decisions, defined integration flows, optimized cloud resources, and ensured security best practices (🔒 OWASP, OAuth2, encryption).&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Effective communication: from technical design to business understanding
&lt;/h2&gt;

&lt;p&gt;One of my strengths as a senior developer has been translating complex technical solutions into understandable explanations for different audiences: from product managers and QA teams to non-technical stakeholders.&lt;/p&gt;

&lt;h3&gt;
  
  
  🛠️ How do I communicate solutions?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Clear user stories&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🧾 &lt;strong&gt;Visuals and diagrams&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🗣️ &lt;strong&gt;Audience-adapted language&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  🔹 Ormigga
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;I visualized the full purchase order flow for different roles (buyer, supplier, financial manager) with real examples and scenarios&lt;/li&gt;
&lt;li&gt;This reduced friction in the approval process and allowed validating the solution before full development was completed&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🚀 Use cases for NestJS, Laravel, Docker, and AWS
&lt;/h2&gt;




&lt;h3&gt;
  
  
  🧱 NestJS
&lt;/h3&gt;

&lt;p&gt;I’ve used &lt;strong&gt;NestJS&lt;/strong&gt; in several projects, for example in &lt;strong&gt;Tres Astronautas&lt;/strong&gt; , where I developed a REST API for property management. There, I applied:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Modular architecture&lt;/li&gt;
&lt;li&gt;✅ Dependency injection&lt;/li&gt;
&lt;li&gt;✅ Use of DTOs and Guards&lt;/li&gt;
&lt;li&gt;✅ Unit testing with Jest&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🛠 Laravel
&lt;/h3&gt;

&lt;p&gt;I’ve worked extensively with &lt;strong&gt;Laravel&lt;/strong&gt; , including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In &lt;strong&gt;Ormigga&lt;/strong&gt; : I designed the full architecture of a B2B purchasing platform with:

&lt;ul&gt;
&lt;li&gt;🔐 User roles&lt;/li&gt;
&lt;li&gt;📣 Real-time notifications&lt;/li&gt;
&lt;li&gt;✅ Multi-step approval flow&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Implementation of:

&lt;ul&gt;
&lt;li&gt;🔗 RESTful API&lt;/li&gt;
&lt;li&gt;🛡 OAuth2 authentication&lt;/li&gt;
&lt;li&gt;⚙️ Job queues&lt;/li&gt;
&lt;li&gt;🧪 Testing with PHPUnit&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🐳 Docker
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Defined consistent development and deployment environments&lt;/li&gt;
&lt;li&gt;Projects with microservices using Laravel and NodeJS&lt;/li&gt;
&lt;li&gt;🔧 Used Docker Compose for local orchestration&lt;/li&gt;
&lt;li&gt;📦 Optimized images for production&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  ☁️ AWS
&lt;/h3&gt;

&lt;p&gt;I’ve worked with multiple &lt;strong&gt;Amazon Web Services&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🖥 &lt;strong&gt;EC2&lt;/strong&gt; : Auto-scaled servers for critical applications&lt;/li&gt;
&lt;li&gt;🗂 &lt;strong&gt;S3&lt;/strong&gt; : File, image, and backup storage&lt;/li&gt;
&lt;li&gt;🛢 &lt;strong&gt;RDS&lt;/strong&gt; : PostgreSQL and MySQL databases with high availability&lt;/li&gt;
&lt;li&gt;⚡ &lt;strong&gt;Lambda&lt;/strong&gt; : Serverless functions for specific tasks&lt;/li&gt;
&lt;li&gt;📊 &lt;strong&gt;DynamoDB&lt;/strong&gt; : For NoSQL workloads and fast queries&lt;/li&gt;
&lt;li&gt;🐳 &lt;strong&gt;ECS Fargate&lt;/strong&gt; : Run containers without managing servers&lt;/li&gt;
&lt;li&gt;🛠 Infrastructure as Code (IaC) for reproducible environments&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;All of this allows me to deliver robust, scalable, and well-maintained solutions 💪.&lt;/p&gt;

</description>
      <category>career</category>
      <category>interview</category>
      <category>softwareengineering</category>
    </item>
  </channel>
</rss>
