<?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>Forem: Michael Masterson</title>
    <description>The latest articles on Forem by Michael Masterson (@mgmaster24).</description>
    <link>https://forem.com/mgmaster24</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%2F1300750%2F855d26cd-b64b-4a78-8fd6-2887f4c4bc2a.png</url>
      <title>Forem: Michael Masterson</title>
      <link>https://forem.com/mgmaster24</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/mgmaster24"/>
    <language>en</language>
    <item>
      <title>I Choose Angular Over React for Large Projects. Here’s Why.</title>
      <dc:creator>Michael Masterson</dc:creator>
      <pubDate>Wed, 29 Apr 2026 01:55:37 +0000</pubDate>
      <link>https://forem.com/mgmaster24/i-choose-angular-over-react-for-large-projects-heres-why-194b</link>
      <guid>https://forem.com/mgmaster24/i-choose-angular-over-react-for-large-projects-heres-why-194b</guid>
      <description>&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.amazonaws.com%2Fuploads%2Farticles%2F429lrepwjuu74nhffh2p.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.amazonaws.com%2Fuploads%2Farticles%2F429lrepwjuu74nhffh2p.png" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I know what you’re thinking. The job boards say React. The bootcamps say React. Every “top frameworks 2025” listicle says React. I’m not here to tell you React is bad — it isn’t. But after 15 years across five industries, teams ranging from 3 to 20 engineers, and frontend codebases that had to survive long past their original authors, I keep reaching for Angular when the stakes are high. Here’s the honest case for why.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let me establish some credibility first
&lt;/h3&gt;

&lt;p&gt;I’m not an Angular developer who hasn’t touched React. I’ve shipped React in production — TypeScript, hooks, the whole ecosystem — on real teams with real users. I know the flexibility. I’ve felt the speed of spinning something up.&lt;/p&gt;

&lt;p&gt;I’ve also led Angular teams building platforms and component libraries that are still in production today, maintained by engineers who had nothing to do with writing them. That last sentence is the whole argument, really.&lt;/p&gt;

&lt;h3&gt;
  
  
  React’s ecosystem is a feature. On large teams, it’s also a trap.
&lt;/h3&gt;

&lt;p&gt;React gives you a rendering library and says “figure out the rest.” For a senior engineer who has already figured it out, that’s liberating. For a 15-person team with mixed experience levels, it’s a slow-motion debate club.&lt;/p&gt;

&lt;p&gt;Pick your state management: Redux? Zustand? Jotai? Recoil? MobX? Context with useReducer? Each has passionate advocates and legitimate use cases. So your team has a meeting. Then another. Someone posts a benchmark. Someone else posts a counter-benchmark. Two weeks later you’ve adopted Zustand, three engineers aren’t sure why, and two secretly wish you’d picked Redux because that’s what they know.&lt;/p&gt;

&lt;p&gt;Now do the same exercise for data fetching. React Query or SWR? RTK Query? Plain useEffect with a custom hook? Axios or fetch? And folder structure — features-based or type-based? Where do shared utilities live? What about form handling — React Hook Form, Formik, or something homegrown?&lt;/p&gt;

&lt;p&gt;None of these are wrong questions. All of them are genuinely debatable. But on a large team, every unanswered convention is a future argument, a future inconsistency, and a future onboarding headache. The flexibility that makes React feel fast in a solo project accumulates into friction at scale.&lt;/p&gt;

&lt;p&gt;Angular answers most of these questions before you write a single line of code. Services for state and data. HttpClient for API calls. Reactive Forms for complex form handling. A CLI that generates consistent, predictable scaffolding. You can disagree with Angular’s answers — but everyone on your team is working with the same answers, and that consistency compounds over years.&lt;/p&gt;

&lt;h3&gt;
  
  
  Onboarding is where this really shows up
&lt;/h3&gt;

&lt;p&gt;I’ve watched senior engineers drop into large React codebases and spend weeks just mapping the terrain. Not because they weren’t capable — they absolutely were — but because every codebase has made different decisions. Different state patterns, different folder conventions, different data-fetching abstractions, all layered on top of each other as the team’s opinions evolved over time. There’s no map. You have to draw your own.&lt;/p&gt;

&lt;p&gt;With Angular, I’ve handed a junior developer a quick tutorial and some guidance and watched them make meaningful contributions within days. Not because Angular is simpler — it isn’t — but because the conventions are already there. A new engineer knows where services live, knows how components are structured, knows how to inject a dependency. The framework is the map.&lt;/p&gt;

&lt;p&gt;The guardrails aren’t limitations. They’re structure. And structure is what lets engineers at every level focus on solving the actual problem instead of navigating codebase archaeology.&lt;/p&gt;

&lt;h3&gt;
  
  
  Angular isn’t your grandfather’s framework
&lt;/h3&gt;

&lt;p&gt;If the last time you seriously looked at Angular was the AngularJS era or even Angular 2–8, you’re carrying a mental model that doesn’t match the current reality. The framework has evolved dramatically.&lt;/p&gt;

&lt;p&gt;Standalone components removed the NgModule overhead that used to be one of Angular’s most common friction points. Now a component declares its own dependencies directly — you open one file and see everything it needs. No hunting through module declarations to understand the dependency graph. With modern IDE tooling, jumping to any dependency is a keystroke away.&lt;/p&gt;

&lt;p&gt;Signals brought a modern, explicit reactivity model that rivals anything in the React ecosystem. Zone.js is becoming optional. The bundle sizes are competitive. The developer experience — template type checking, language service, CLI — has matured to the point where it genuinely gets out of your way.&lt;/p&gt;

&lt;p&gt;The Angular of 2026 is not what critics are usually criticizing. The framework has quietly had one of the most impressive evolution arcs in frontend history, and a lot of the React community hasn’t noticed because they stopped paying attention a few versions ago.&lt;/p&gt;

&lt;h3&gt;
  
  
  TypeScript first, not TypeScript adjacent
&lt;/h3&gt;

&lt;p&gt;Angular was designed with TypeScript from the ground up. The compiler catches template type errors at build time — pass the wrong type to a component input, reference a property that doesn’t exist in your template, and the build fails. You find out immediately, not in production.&lt;/p&gt;

&lt;p&gt;React with TypeScript is genuinely good. But TypeScript is optional in React, and optionality in a large codebase is a liability. You’ll inherit files written before TypeScript was added. You’ll find any used as an escape hatch. You'll see prop interfaces that diverged from reality months ago. Angular's compiler is less forgiving — and on a large team, less forgiving is a feature.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependency injection changes how you architect
&lt;/h3&gt;

&lt;p&gt;This is the one React developers tend to wave away until they’ve lived with it. Angular’s DI system makes service boundaries explicit. You define a service, declare where it lives in the injector hierarchy, inject it where you need it. Testing becomes trivial — swap the real service for a mock and your component tests have no idea the difference.&lt;/p&gt;

&lt;p&gt;React’s answer is context, custom hooks, and discipline. Context works. Custom hooks are elegant. But discipline is not a framework feature — it degrades over time, especially as teams grow and change. I’ve seen React codebases where data fetching logic had migrated into components because “just this once,” and that pattern had replicated itself across dozens of files. Angular’s DI doesn’t prevent bad decisions, but it makes the good patterns the path of least resistance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where React genuinely wins
&lt;/h3&gt;

&lt;p&gt;Intellectual honesty matters here. React is the right call in real scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ecosystem breadth — more third-party libraries, more community examples, and frankly more AI awareness. React’s dominance means LLMs have seen more React code, which matters when AI-assisted development is part of your workflow.&lt;/li&gt;
&lt;li&gt;Hiring — more React engineers available, and that matters when you’re scaling fast&lt;/li&gt;
&lt;li&gt;Rapid prototyping — React plus Next.js is hard to beat for getting something live quickly&lt;/li&gt;
&lt;li&gt;React Native — if mobile is in scope, the story is much cleaner&lt;/li&gt;
&lt;li&gt;Small teams with strong opinions — if everyone has been writing React for years with aligned conventions, you may not feel the pain points I’m describing&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The framework isn’t the bottleneck. The team is.
&lt;/h3&gt;

&lt;p&gt;The right framework is the one your team can stay consistent with at your current size and growth rate.&lt;/p&gt;

&lt;p&gt;Small team, short time horizon, everyone senior — React’s flexibility is an asset. The overhead of Angular’s conventions may slow you down more than the structure helps.&lt;/p&gt;

&lt;p&gt;Large team, long time horizon, mixed experience levels — Angular’s conventions are load-bearing. The structure isn’t overhead, it’s the thing that keeps twenty engineers pointing in the same direction eighteen months from now.&lt;/p&gt;

&lt;p&gt;I’ve been on both sides of this. I know which late-night debugging sessions I’d rather not repeat.&lt;/p&gt;

&lt;p&gt;At M²S², we help engineering teams make exactly these kinds of decisions — not based on what’s trending, but based on what actually works at your scale. If you’re weighing your options, &lt;a href="https://blog.m2s2.io/contact" rel="noopener noreferrer"&gt;let’s talk&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>frontend</category>
      <category>angular</category>
    </item>
    <item>
      <title>Using Claude to Move Faster Without Cutting Corners</title>
      <dc:creator>Michael Masterson</dc:creator>
      <pubDate>Tue, 28 Apr 2026 15:46:37 +0000</pubDate>
      <link>https://forem.com/mgmaster24/using-claude-to-move-faster-without-cutting-corners-3ii2</link>
      <guid>https://forem.com/mgmaster24/using-claude-to-move-faster-without-cutting-corners-3ii2</guid>
      <description>&lt;p&gt;There's a version of AI-assisted development that looks like this: you describe a feature, the AI writes the code, you paste it in, it mostly works, you move on. That's one way to use it. It's not the most valuable way.&lt;/p&gt;

&lt;p&gt;I use Claude daily — for architecture decisions, code review, writing, debugging, and building features. The productivity gains are real, but they don't come from offloading thinking. They come from having a capable collaborator available at every point in the process, one that never gets tired of context, never judges a half-formed question, and can hold an entire codebase in mind while you work through a problem together.&lt;/p&gt;

&lt;p&gt;Here's what that actually looks like in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude as a thinking partner, not an autocomplete engine
&lt;/h2&gt;

&lt;p&gt;The frame that changed how I use AI tools: Claude isn't a faster way to type. It's a faster way to think.&lt;/p&gt;

&lt;p&gt;When I'm designing a new system, I'll describe the constraints and ask for pushback on my assumptions before writing a line of code. When I'm debugging something subtle, I'll walk through what I know and ask what I might be missing. When I'm about to make an architectural decision, I'll ask for the tradeoffs I haven't considered yet. None of that is code generation — it's structured thinking with a collaborator who has broad context and no ego.&lt;/p&gt;

&lt;p&gt;This distinction matters because developers who treat AI as autocomplete get autocomplete-level value. Developers who treat it as a thinking partner get something closer to a senior engineer always available in the room.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually works
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Architecture and design.&lt;/strong&gt; Before writing code, I describe what I'm building — the constraints, the scale, the tradeoffs I'm aware of. Claude is good at surfacing tradeoffs I haven't considered and poking holes in approaches that seem solid but have hidden failure modes. This has saved me from several decisions that would have been expensive to reverse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Boilerplate and scaffolding.&lt;/strong&gt; The parts of development that are mechanical — setting up a new Lambda handler, wiring a new route, writing a test harness — Claude handles quickly and correctly. This isn't the interesting part of the work, and not spending mental energy on it means more for the parts that matter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code review and refactoring.&lt;/strong&gt; I'll share a function and ask what I'm missing — edge cases, error conditions, performance issues, readability problems. It catches things I'm too close to see. It's not infallible, but the hit rate is high enough that I treat it as a mandatory step before pushing anything significant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Writing.&lt;/strong&gt; Documentation, commit messages, technical explanations, posts like this one. Having a collaborator who can help sharpen an argument or tighten a paragraph without changing the voice is genuinely useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  I don't vibe code
&lt;/h2&gt;

&lt;p&gt;There's a style of AI-assisted development that's become popular — describe what you want, accept whatever comes out, repeat. I don't work that way.&lt;/p&gt;

&lt;p&gt;My process is closer to: prompt, plan, review. Before any significant piece of work, I'll use Claude's plan mode to lay out the approach — what files change, what the logic looks like, what the edge cases are. I read the plan. I push back on parts that don't look right. I ask questions. Only once I understand and agree with the direction do I let it execute.&lt;/p&gt;

&lt;p&gt;The same applies to output. Every piece of code Claude produces gets reviewed before it lands. Not a quick skim — an actual read. I want to understand what it did and why. If I can't explain it, I ask until I can. This isn't distrust, it's ownership. Code I don't understand is a liability I'm carrying.&lt;/p&gt;

&lt;p&gt;The developers who get burned by AI tooling are usually the ones who skipped this step. The ones who get consistent value treat the AI output as a strong first draft from a collaborator — not a finished answer from an authority.&lt;/p&gt;

&lt;h2&gt;
  
  
  The skill that actually determines your results
&lt;/h2&gt;

&lt;p&gt;Prompting is a real skill, and it compounds. The developers getting the most out of Claude aren't the ones with the most AI experience — they're the ones who communicate precisely, provide relevant context, and ask specific questions instead of vague ones.&lt;/p&gt;

&lt;p&gt;"Fix this function" gets worse results than "This function is supposed to do X, it's currently doing Y in edge case Z, here's what I've already tried." The more precisely you can describe what you know, what you want, and what constraints matter, the better the output.&lt;/p&gt;

&lt;p&gt;The fastest way to improve: treat every unsatisfying response as a prompt quality problem first. Reframe the question, add context, be more specific about what you actually need. You'll get better at it quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The meta point
&lt;/h2&gt;

&lt;p&gt;The platform this blog runs on — the Angular frontend, the Go Lambda API, the CDK infrastructure — was built with Claude as a constant collaborator. Not generated wholesale, but developed in a genuine back-and-forth: I set the direction and made the decisions, Claude helped me move faster and caught things I missed. The result is code I understand completely and can maintain confidently.&lt;/p&gt;

&lt;p&gt;That's the model I'd advocate for. AI in service of your judgment, not a replacement for it.&lt;/p&gt;

&lt;p&gt;At M²S² Engineering Group, we build with the best tools available — including AI. If you're figuring out how to integrate these workflows into your team's practice, &lt;a href="https://dev.to/contact"&gt;let's talk&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://m2s2.io/blog/claude-assisted-development" rel="noopener noreferrer"&gt;m2s2.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>claude</category>
      <category>devrel</category>
    </item>
    <item>
      <title>Modern Angular Architecture: From NgModules to Standalone Components</title>
      <dc:creator>Michael Masterson</dc:creator>
      <pubDate>Tue, 28 Apr 2026 15:46:35 +0000</pubDate>
      <link>https://forem.com/mgmaster24/modern-angular-architecture-from-ngmodules-to-standalone-components-3fla</link>
      <guid>https://forem.com/mgmaster24/modern-angular-architecture-from-ngmodules-to-standalone-components-3fla</guid>
      <description>&lt;p&gt;NgModules were Angular's answer to a real problem: how do you organize a large application into logical units, share dependencies, and lazy-load chunks of functionality? The answer worked. It was also verbose, confusing to newcomers, and responsible for some of the most baffling error messages in frontend development.&lt;/p&gt;

&lt;p&gt;Standalone components — stable since Angular 15, the default since Angular 17 — solve the same problems with significantly less ceremony. I've migrated a production application through this transition and built new ones from scratch with the standalone model. Here's what actually matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mental model shift
&lt;/h2&gt;

&lt;p&gt;In the NgModule world, a component is a member of a module. The module declares what the component can use. You want to use &lt;code&gt;RouterLink&lt;/code&gt; in a component? Add &lt;code&gt;RouterModule&lt;/code&gt; to the module's imports. You want to use another component? Either declare it in the same module or import the module that exports it.&lt;/p&gt;

&lt;p&gt;This creates a layer of indirection that makes components non-portable. A component's dependencies are defined somewhere else, by someone else, possibly a long time ago. New team members spend their first weeks asking "why can't I use X here" and the answer is always somewhere in a module file.&lt;/p&gt;

&lt;p&gt;In the standalone model, the component owns its dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./dashboard.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;NgIf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NgFor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NgClass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RouterLink&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DashboardComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything the template needs is declared right here. You can open this file cold and know exactly what it depends on without hunting through module trees. That's not a small thing — it compounds across a codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  inject() over constructor injection
&lt;/h2&gt;

&lt;p&gt;Standalone components pair naturally with the &lt;code&gt;inject()&lt;/code&gt; function, which lets you inject dependencies without a constructor parameter list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Old pattern&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;authService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AuthService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;contentService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ContentService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// New pattern&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;authService&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AuthService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;contentService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ContentService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This isn't just a style preference. &lt;code&gt;inject()&lt;/code&gt; works in any injection context — component constructors, service factories, route guards — which means you can extract reusable injection logic into plain functions without creating a service class for it. It also makes the dependencies easier to read at a glance since each one is on its own line with its type visible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signals: the reactive layer that finally makes sense
&lt;/h2&gt;

&lt;p&gt;Angular 16 introduced signals as a new reactive primitive, and they fit the standalone model naturally. Where you'd previously reach for &lt;code&gt;BehaviorSubject&lt;/code&gt; or complex &lt;code&gt;async&lt;/code&gt; pipe chains, signals give you a simpler model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AdminComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;inquiries&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Inquiry&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;activeTab&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inquiries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;visitors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inquiries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// computed() derives state — recalculates only when dependencies change&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;inquiryCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inquiries&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key insight: a signal is just a value with change notification built in. You read it by calling it like a function — &lt;code&gt;this.loading()&lt;/code&gt; — and write it with &lt;code&gt;.set()&lt;/code&gt; or &lt;code&gt;.update()&lt;/code&gt;. Angular tracks which signals a template reads and re-renders only when those specific signals change.&lt;/p&gt;

&lt;p&gt;For auth state in particular, signals are a significant improvement. Rather than maintaining a subscription and manually updating component state, you expose a signal from a service and components derive from it with &lt;code&gt;computed()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Service&lt;/span&gt;
&lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SessionInfo&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UNAUTHENTICATED&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Component — updates automatically whenever session changes&lt;/span&gt;
&lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;navConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;buildConfigForRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No subscriptions. No &lt;code&gt;ngOnDestroy&lt;/code&gt;. No memory leaks from forgotten unsubscribes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lazy loading without modules
&lt;/h2&gt;

&lt;p&gt;Lazy loading used to require a routing module and a &lt;code&gt;loadChildren&lt;/code&gt; callback that imported it. Now it's a single line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;loadChildren&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./dashboard/dashboard.module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DashboardModule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// After&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;loadComponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./dashboard/dashboard.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DashboardComponent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bundle split is identical. The module is gone. For route groups you still have &lt;code&gt;loadChildren&lt;/code&gt;, but it now accepts an array of routes rather than a module.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migration strategy that actually works
&lt;/h2&gt;

&lt;p&gt;If you're migrating an existing app, go leaf-first. Start with components that have no children of their own — the deepest nodes in the component tree. Add &lt;code&gt;standalone: true&lt;/code&gt; and move their dependencies into the component's &lt;code&gt;imports&lt;/code&gt; array. Then work inward toward shared components, then feature components, then the root.&lt;/p&gt;

&lt;p&gt;The Angular CLI has a migration schematic that handles most of this automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng generate @angular/core:standalone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it in stages — it gives you options for converting components, removing unnecessary NgModule imports, and bootstrapping as standalone. Use &lt;code&gt;git diff&lt;/code&gt; aggressively between each stage to review what changed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CommonModule imports.&lt;/strong&gt; You'll find &lt;code&gt;CommonModule&lt;/code&gt; in a lot of module import lists because it's the easy way to get &lt;code&gt;NgIf&lt;/code&gt;, &lt;code&gt;NgFor&lt;/code&gt;, and the async pipe. In standalone components, import only what you need — &lt;code&gt;NgIf&lt;/code&gt;, &lt;code&gt;NgFor&lt;/code&gt;, &lt;code&gt;AsyncPipe&lt;/code&gt; — individually. It's more verbose initially but makes tree-shaking more effective.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shared services don't change.&lt;/strong&gt; Services with &lt;code&gt;providedIn: 'root'&lt;/code&gt; work exactly the same. You don't need to touch them. The standalone migration is a component-level change, not a service-level one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTTP interceptors move to bootstrapApplication.&lt;/strong&gt; If you use HTTP interceptors, they're now registered with &lt;code&gt;withInterceptors()&lt;/code&gt; in the &lt;code&gt;bootstrapApplication&lt;/code&gt; call rather than in a module provider array. Easy to miss, and it fails silently if you forget.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third-party libraries may lag.&lt;/strong&gt; Some Angular libraries still export NgModules and haven't published standalone-compatible exports. You can still import a module into a standalone component's &lt;code&gt;imports&lt;/code&gt; array — it works — but check whether newer versions of the library offer standalone APIs before importing the whole module.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is it worth the migration effort?
&lt;/h2&gt;

&lt;p&gt;For a new project: yes, unambiguously. Build standalone from the start. The mental model is simpler, the tooling assumes it, and new Angular features are being designed around it.&lt;/p&gt;

&lt;p&gt;For an existing app: it depends on the size and stability of the codebase. A large, stable NgModule-based app that isn't causing problems doesn't urgently need migration. A growing codebase with new features being added regularly will benefit from the migration — but do it incrementally, not all at once.&lt;/p&gt;

&lt;p&gt;At M²S² Engineering Group, we help teams make exactly these kinds of decisions — whether to migrate, how to sequence it, and how to do it without disrupting ongoing delivery. If you're navigating a modernization and want to talk it through, &lt;a href="https://dev.to/contact"&gt;let's talk&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://m2s2.io/blog/angular-standalone-architecture" rel="noopener noreferrer"&gt;m2s2.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>typescript</category>
      <category>frontend</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Engineering Leadership: What Nobody Tells You Before You Take the Job</title>
      <dc:creator>Michael Masterson</dc:creator>
      <pubDate>Tue, 28 Apr 2026 14:32:14 +0000</pubDate>
      <link>https://forem.com/mgmaster24/engineering-leadership-what-nobody-tells-you-before-you-take-the-job-3c59</link>
      <guid>https://forem.com/mgmaster24/engineering-leadership-what-nobody-tells-you-before-you-take-the-job-3c59</guid>
      <description>&lt;p&gt;I didn't plan to become an engineering manager. I was a senior engineer who cared deeply about how the team worked, and at some point that care turned into a title. What followed was a crash course in everything a technical background doesn't prepare you for.&lt;/p&gt;

&lt;p&gt;I've led teams of 9 and 20 — onshore, nearshore, and offshore — through greenfield builds, legacy migrations, platform rewrites, and ambiguous mandates with moving deadlines. These are the things I wish someone had told me before I started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your output is no longer code
&lt;/h2&gt;

&lt;p&gt;This sounds obvious. It isn't, really — not until you feel it. Early in my first management role I kept finding myself drifting back to the IDE. It felt productive. It was familiar. It was also the wrong use of my time.&lt;/p&gt;

&lt;p&gt;As an IC, your output is what you ship. As a manager, your output is what your team ships. Those are different jobs. The sooner you internalize that, the sooner you start spending your time on the things that actually multiply the team's output — clearing blockers, making decisions, setting direction, hiring well, and building the kind of environment where engineers can do their best work without constantly checking with you first.&lt;/p&gt;

&lt;p&gt;That last part — reducing your own necessity — is the hardest thing to learn and the most important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ambiguity is the job, not an obstacle to it
&lt;/h2&gt;

&lt;p&gt;Engineers are trained to solve well-defined problems. Management is largely the practice of operating in conditions where the problem isn't well-defined, the requirements will change, and someone above you wants a date.&lt;/p&gt;

&lt;p&gt;I've been handed teams mid-rebuild with mandates to modernize platforms that had been accumulating technical debt for years. Scope changed constantly. Stakeholder priorities shifted week to week. There was no clear finish line. The job was to keep the team moving productively despite that, not to wait for clarity that was never coming.&lt;/p&gt;

&lt;p&gt;The engineers who struggled most in that environment were the ones waiting for permission to move. The ones who thrived understood that forward motion with imperfect information beats paralysis while waiting for a perfect spec. Your job as a leader is to model that — and to create enough safety that your team feels the same way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trust is built in small moments, not big ones
&lt;/h2&gt;

&lt;p&gt;I've seen managers try to build team trust through all-hands talks, offsites, and team-building events. Those things aren't bad, but they're not where trust actually gets built.&lt;/p&gt;

&lt;p&gt;Trust is built when you fight for someone's promotion and they find out. When you take the blame in a postmortem instead of pointing at the engineer who made the call. When you remember what someone said in a 1:1 three weeks ago and follow up on it without being asked. When you give direct feedback early instead of letting something fester into a performance conversation six months later.&lt;/p&gt;

&lt;p&gt;None of those are dramatic moments. They're small, repeated actions over time. That's what trust is made of.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical direction has to be set, not consensus-built
&lt;/h2&gt;

&lt;p&gt;There's a version of technical leadership where every architecture decision goes through the whole team, everyone has a voice, and nothing gets decided until there's buy-in. I understand the instinct. It backfires constantly.&lt;/p&gt;

&lt;p&gt;Teams move fast when they have clear guardrails — approved patterns, defined standards, explicit rules about what's off-limits — and then autonomy inside those guardrails. What they can't do is move fast when every decision requires a committee.&lt;/p&gt;

&lt;p&gt;I've defined frontend and backend standards that became the baseline for all platform development across teams. Engineers didn't need to ask whether to use a certain pattern — the answer was already documented. That eliminated an enormous amount of friction. The team made more decisions independently, shipped more consistently, and had better code quality than any team I'd seen operate by pure committee.&lt;/p&gt;

&lt;p&gt;This doesn't mean you don't listen. It means you listen, decide, document, and then hold the line — revisiting when there's real evidence the direction needs to change, not just because someone prefers a different approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hiring is the highest-leverage thing you do
&lt;/h2&gt;

&lt;p&gt;Everything else on this list — direction, trust, standards, culture — compounds if you have the right people and collapses if you don't. One mis-hire in a key role can consume more of your time and team energy than any technical problem you'll face.&lt;/p&gt;

&lt;p&gt;I've hired dozens of engineers across multiple organizations. The most effective change I made was introducing AI-assisted assessments that gave consistent, role-specific evaluations for every candidate. The result was a more defensible hiring bar, less prep time, and — critically — fewer late-stage surprises when someone's actual ability didn't match how they presented in an interview.&lt;/p&gt;

&lt;p&gt;What I look for beyond technical skill: how does someone operate when they don't have enough information? Do they ask good questions or just wait? Can they disagree directly without being difficult? Have they shipped something that actually worked? Those signals matter more than algorithm fluency at a whiteboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing up is a skill, not a concession
&lt;/h2&gt;

&lt;p&gt;Early on I thought managing up — keeping stakeholders informed, shaping expectations, making the team's work legible to leadership — was a distraction from real work. I was wrong.&lt;/p&gt;

&lt;p&gt;Leadership above you is making decisions with incomplete information. If you're not providing that information, someone else is — and their version of events may not reflect what's actually happening. Proactively communicating status, surfacing risks before they're crises, and framing technical trade-offs in business terms isn't politics. It's protecting your team from decisions made in a vacuum.&lt;/p&gt;

&lt;p&gt;The cleaner your communication upward, the more autonomy you earn downward. That's the deal.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thing that actually determines whether a team ships
&lt;/h2&gt;

&lt;p&gt;After years of this, I've come to believe that the single biggest predictor of team output isn't process, tooling, or even talent. It's psychological safety — the degree to which engineers feel safe raising problems, asking questions, and being wrong in front of each other.&lt;/p&gt;

&lt;p&gt;Teams with high safety surface bugs earlier. They escalate sooner. They try things that might not work. They tell you when a deadline isn't realistic instead of staying quiet and missing it. Teams without safety hide problems until they're catastrophic, defer hard conversations, and optimize for looking good over doing good work.&lt;/p&gt;

&lt;p&gt;You create it by being honest about what you don't know. By treating mistakes as information rather than failures. By rewarding the person who raised a concern early over the person who stayed quiet and got lucky. It takes a long time to build and almost no time to destroy.&lt;/p&gt;

&lt;p&gt;At M²S² Engineering Group, engineering leadership is part of every engagement — not an afterthought. Whether you need an embedded partner or someone to help set direction, &lt;a href="https://dev.to/contact"&gt;let's talk about what that looks like&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://m2s2.io/blog/engineering-leadership-lessons" rel="noopener noreferrer"&gt;m2s2.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>management</category>
      <category>teams</category>
      <category>career</category>
    </item>
    <item>
      <title>I Choose Angular Over React for Large Projects. Here's Why.</title>
      <dc:creator>Michael Masterson</dc:creator>
      <pubDate>Mon, 27 Apr 2026 21:40:40 +0000</pubDate>
      <link>https://forem.com/mgmaster24/i-choose-angular-over-react-for-large-projects-heres-why-1b30</link>
      <guid>https://forem.com/mgmaster24/i-choose-angular-over-react-for-large-projects-heres-why-1b30</guid>
      <description>&lt;p&gt;I know what you're thinking. The job boards say React. The bootcamps say React. Every "top frameworks 2025" listicle says React. I'm not here to tell you React is bad — it isn't. But after 15 years across five industries, teams ranging from 3 to 20 engineers, and frontend codebases that had to survive long past their original authors, I keep reaching for Angular when the stakes are high. Here's the honest case for why.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let me establish some credibility first
&lt;/h2&gt;

&lt;p&gt;I'm not an Angular developer who hasn't touched React. I've shipped React in production — TypeScript, hooks, the whole ecosystem — on real teams with real users. I know the flexibility. I've felt the speed of spinning something up.&lt;/p&gt;

&lt;p&gt;I've also led Angular teams building platforms and component libraries that are still in production today, maintained by engineers who had nothing to do with writing them. That last sentence is the whole argument, really.&lt;/p&gt;

&lt;h2&gt;
  
  
  React's ecosystem is a feature. On large teams, it's also a trap.
&lt;/h2&gt;

&lt;p&gt;React gives you a rendering library and says "figure out the rest." For a senior engineer who has already figured it out, that's liberating. For a 15-person team with mixed experience levels, it's a slow-motion debate club.&lt;/p&gt;

&lt;p&gt;Pick your state management: Redux? Zustand? Jotai? Recoil? MobX? Context with useReducer? Each has passionate advocates and legitimate use cases. So your team has a meeting. Then another. Someone posts a benchmark. Someone else posts a counter-benchmark. Two weeks later you've adopted Zustand, three engineers aren't sure why, and two secretly wish you'd picked Redux because that's what they know.&lt;/p&gt;

&lt;p&gt;Now do the same exercise for data fetching. React Query or SWR? RTK Query? Plain useEffect with a custom hook? Axios or fetch? And folder structure — features-based or type-based? Where do shared utilities live? What about form handling — React Hook Form, Formik, or something homegrown?&lt;/p&gt;

&lt;p&gt;None of these are wrong questions. All of them are genuinely debatable. But on a large team, every unanswered convention is a future argument, a future inconsistency, and a future onboarding headache. The flexibility that makes React feel fast in a solo project accumulates into friction at scale.&lt;/p&gt;

&lt;p&gt;Angular answers most of these questions before you write a single line of code. Services for state and data. HttpClient for API calls. Reactive Forms for complex form handling. A CLI that generates consistent, predictable scaffolding. You can disagree with Angular's answers — but everyone on your team is working with the same answers, and that consistency compounds over years.&lt;/p&gt;

&lt;h2&gt;
  
  
  Onboarding is where this really shows up
&lt;/h2&gt;

&lt;p&gt;I've watched senior engineers drop into large React codebases and spend weeks just mapping the terrain. Not because they weren't capable — they absolutely were — but because every codebase has made different decisions. Different state patterns, different folder conventions, different data-fetching abstractions, all layered on top of each other as the team's opinions evolved over time. There's no map. You have to draw your own.&lt;/p&gt;

&lt;p&gt;With Angular, I've handed a junior developer a quick tutorial and some guidance and watched them make meaningful contributions within days. Not because Angular is simpler — it isn't — but because the conventions are already there. A new engineer knows where services live, knows how components are structured, knows how to inject a dependency. The framework is the map.&lt;/p&gt;

&lt;p&gt;The guardrails aren't limitations. They're structure. And structure is what lets engineers at every level focus on solving the actual problem instead of navigating codebase archaeology.&lt;/p&gt;

&lt;h2&gt;
  
  
  Angular isn't your grandfather's framework
&lt;/h2&gt;

&lt;p&gt;If the last time you seriously looked at Angular was the AngularJS era or even Angular 2–8, you're carrying a mental model that doesn't match the current reality. The framework has evolved dramatically.&lt;/p&gt;

&lt;p&gt;Standalone components removed the NgModule overhead that used to be one of Angular's most common friction points. Now a component declares its own dependencies directly — you open one file and see everything it needs. No hunting through module declarations to understand the dependency graph. With modern IDE tooling, jumping to any dependency is a keystroke away.&lt;/p&gt;

&lt;p&gt;Signals brought a modern, explicit reactivity model that rivals anything in the React ecosystem. Zone.js is becoming optional. The bundle sizes are competitive. The developer experience — template type checking, language service, CLI — has matured to the point where it genuinely gets out of your way.&lt;/p&gt;

&lt;p&gt;The Angular of 2026 is not what critics are usually criticizing. The framework has quietly had one of the most impressive evolution arcs in frontend history, and a lot of the React community hasn't noticed because they stopped paying attention a few versions ago.&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript first, not TypeScript adjacent
&lt;/h2&gt;

&lt;p&gt;Angular was designed with TypeScript from the ground up. The compiler catches template type errors at build time — pass the wrong type to a component input, reference a property that doesn't exist in your template, and the build fails. You find out immediately, not in production.&lt;/p&gt;

&lt;p&gt;React with TypeScript is genuinely good. But TypeScript is optional in React, and optionality in a large codebase is a liability. You'll inherit files written before TypeScript was added. You'll find &lt;code&gt;any&lt;/code&gt; used as an escape hatch. You'll see prop interfaces that diverged from reality months ago. Angular's compiler is less forgiving — and on a large team, less forgiving is a feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependency injection changes how you architect
&lt;/h2&gt;

&lt;p&gt;This is the one React developers tend to wave away until they've lived with it. Angular's DI system makes service boundaries explicit. You define a service, declare where it lives in the injector hierarchy, inject it where you need it. Testing becomes trivial — swap the real service for a mock and your component tests have no idea the difference.&lt;/p&gt;

&lt;p&gt;React's answer is context, custom hooks, and discipline. Context works. Custom hooks are elegant. But discipline is not a framework feature — it degrades over time, especially as teams grow and change. I've seen React codebases where data fetching logic had migrated into components because "just this once," and that pattern had replicated itself across dozens of files. Angular's DI doesn't prevent bad decisions, but it makes the good patterns the path of least resistance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where React genuinely wins
&lt;/h2&gt;

&lt;p&gt;Intellectual honesty matters here. React is the right call in real scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ecosystem breadth&lt;/strong&gt; — more third-party libraries, more community examples, and frankly more AI awareness. React's dominance means LLMs have seen more React code, which matters when AI-assisted development is part of your workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hiring&lt;/strong&gt; — more React engineers available, and that matters when you're scaling fast&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rapid prototyping&lt;/strong&gt; — React plus Next.js is hard to beat for getting something live quickly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React Native&lt;/strong&gt; — if mobile is in scope, the story is much cleaner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Small teams with strong opinions&lt;/strong&gt; — if everyone has been writing React for years with aligned conventions, you may not feel the pain points I'm describing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The framework isn't the bottleneck. The team is.
&lt;/h2&gt;

&lt;p&gt;The right framework is the one your team can stay consistent with at your current size and growth rate.&lt;/p&gt;

&lt;p&gt;Small team, short time horizon, everyone senior — React's flexibility is an asset. The overhead of Angular's conventions may slow you down more than the structure helps.&lt;/p&gt;

&lt;p&gt;Large team, long time horizon, mixed experience levels — Angular's conventions are load-bearing. The structure isn't overhead, it's the thing that keeps twenty engineers pointing in the same direction eighteen months from now.&lt;/p&gt;

&lt;p&gt;I've been on both sides of this. I know which late-night debugging sessions I'd rather not repeat.&lt;/p&gt;

&lt;p&gt;At M²S², we help engineering teams make exactly these kinds of decisions — not based on what's trending, but based on what actually works at your scale. If you're weighing your options, &lt;a href="https://dev.to/contact"&gt;let's talk&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://m2s2.io/blog/angular-over-react-large-projects" rel="noopener noreferrer"&gt;m2s2.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>react</category>
      <category>typescript</category>
      <category>frontend</category>
    </item>
    <item>
      <title>What a Fractional Engineering Leader Actually Does</title>
      <dc:creator>Michael Masterson</dc:creator>
      <pubDate>Thu, 23 Apr 2026 00:34:42 +0000</pubDate>
      <link>https://forem.com/mgmaster24/what-a-fractional-engineering-leader-actually-does-3jnp</link>
      <guid>https://forem.com/mgmaster24/what-a-fractional-engineering-leader-actually-does-3jnp</guid>
      <description>&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.amazonaws.com%2Fuploads%2Farticles%2Fatc4nu660lt3zwwjeiiv.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.amazonaws.com%2Fuploads%2Farticles%2Fatc4nu660lt3zwwjeiiv.png" alt="m2s2 eng leadership" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most founders who reach out to a fractional engineering leader have the same question underneath whatever they actually ask: &lt;em&gt;is this a real thing, and is it for me?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It’s a fair question. The term gets used loosely — sometimes it means a part-time hire, sometimes a consultant, sometimes an advisor who shows up to a monthly call. The ambiguity makes it hard to evaluate. So let me be direct about what it actually means in practice.&lt;/p&gt;

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

&lt;p&gt;A fractional engineering leader is a senior technical partner who embeds with your team on a part-time basis — typically a set number of days per week — and takes on real ownership of the engineering function. Not advisory. Not oversight. Actual responsibility for the decisions, direction, and execution quality of what your team builds.&lt;/p&gt;

&lt;p&gt;That means setting technical standards, making architecture calls, running the hiring process, managing engineers, communicating with stakeholders, and being accountable for delivery. The same things a full-time CTO or VP of Engineering would own — delivered at a scope and cost that matches where you actually are.&lt;/p&gt;

&lt;h3&gt;
  
  
  Who it’s for
&lt;/h3&gt;

&lt;p&gt;The companies that get the most out of this model tend to look like one of these:&lt;/p&gt;

&lt;h3&gt;
  
  
  The technical founder who’s outgrown the role.
&lt;/h3&gt;

&lt;p&gt;You built the first version. You’re still the most senior engineer on the team. But you’re also running product, talking to customers, and trying to close deals. Engineering decisions are getting deferred, standards are slipping, and you know it. You need someone to own the technical function so you can focus on the business.&lt;/p&gt;

&lt;h3&gt;
  
  
  The non-technical founder with engineers.
&lt;/h3&gt;

&lt;p&gt;You have a team building something. You can evaluate business outcomes but not the quality of the decisions underneath them. You need a trusted technical partner who can tell you what’s actually going on, make the calls you can’t, and be honest with you when something is heading in the wrong direction.&lt;/p&gt;

&lt;h3&gt;
  
  
  The growing team that needs structure.
&lt;/h3&gt;

&lt;p&gt;You’ve scaled past the point where everything fits in one person’s head, but you’re not ready — or don’t need — a full-time executive. You need patterns, standards, and someone who’s built at this stage before to set the foundation before it becomes expensive to fix.&lt;/p&gt;

&lt;h3&gt;
  
  
  What it actually looks like
&lt;/h3&gt;

&lt;p&gt;The specifics vary by engagement, but the shape is usually consistent: a defined number of days per week, clear ownership of the engineering function, and regular communication with whoever is running the business.&lt;/p&gt;

&lt;p&gt;In practice that means being present in the work — in code reviews, architecture discussions, hiring conversations, and planning sessions — not just showing up to give opinions. It means knowing the codebase, knowing the team, and being accountable for the output. An engagement that stays at the advisory layer isn’t really fractional leadership. It’s expensive consulting.&lt;/p&gt;

&lt;p&gt;The best engagements feel like a founding team member who happens to work a structured schedule. The worst ones have unclear ownership and a leader who isn’t close enough to the work to make good decisions. The difference is usually defined scope and real accountability from the start.&lt;/p&gt;

&lt;h3&gt;
  
  
  What it isn’t
&lt;/h3&gt;

&lt;p&gt;It isn’t a shortcut. A fractional leader can move fast because they’ve seen these problems before, but they still need time to understand your context, your team, and your constraints. Expect a ramp period. Expect to invest in the relationship.&lt;/p&gt;

&lt;p&gt;It also isn’t a replacement for eventually building a full-time engineering leadership function if that’s where you’re headed. The best fractional engagements either solve a specific problem and conclude cleanly, or they set the foundation well enough that bringing on a full-time leader becomes a natural next step — with a codebase, team, and standards in good shape when they arrive.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to know if it’s the right call
&lt;/h3&gt;

&lt;p&gt;A few honest signals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You’re making technical decisions you’re not confident in, and you know it.&lt;/li&gt;
&lt;li&gt;Engineering is moving slower than it should and you can’t pinpoint why. Things are moving fast — maybe too fast — and there’s no senior voice in the room to slow down, ask the hard questions, and make sure the foundation holds up under the pace.&lt;/li&gt;
&lt;li&gt;Your team is growing and nobody is setting the bar for how things get built.&lt;/li&gt;
&lt;li&gt;You’ve interviewed for a full-time CTO and haven’t found the right person, or the role doesn’t justify a full-time salary yet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Any of those is a reasonable starting point for a conversation.&lt;/p&gt;

&lt;p&gt;At M²S² Engineering Group, fractional engineering leadership is one of the core ways we engage — embedded, accountable, and focused on the work that actually moves things forward. If any of this sounds like where you are, &lt;a href="https://m2s2.io/contact" rel="noopener noreferrer"&gt;let’s talk&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>engineeringleadershi</category>
      <category>softwareengineering</category>
      <category>leadership</category>
      <category>fractionalexecutive</category>
    </item>
    <item>
      <title>My Terminal-Native Dev Setup: WezTerm and Neovim</title>
      <dc:creator>Michael Masterson</dc:creator>
      <pubDate>Wed, 22 Apr 2026 13:48:45 +0000</pubDate>
      <link>https://forem.com/mgmaster24/my-terminal-native-dev-setup-wezterm-and-neovim-21h</link>
      <guid>https://forem.com/mgmaster24/my-terminal-native-dev-setup-wezterm-and-neovim-21h</guid>
      <description>&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.amazonaws.com%2Fuploads%2Farticles%2Fuhgj13ejzwm8cowksggw.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.amazonaws.com%2Fuploads%2Farticles%2Fuhgj13ejzwm8cowksggw.png" alt="wezneo" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;April 20, 2026&lt;/p&gt;

&lt;p&gt;Most developers reach for VS Code by default. It’s a reasonable choice — good ecosystem, low friction, gets out of your way. I used it for years. At some point I started paying attention to how much time I spent with my hands off the keyboard, reaching for the mouse to navigate, click through file trees, manage tabs. It adds up.&lt;/p&gt;

&lt;p&gt;I’ve been running WezTerm and Neovim as my primary development environment for a while now. I work across Go, TypeScript, and Angular — a reasonably demanding setup that requires solid LSP support, fast navigation, and a terminal that doesn’t slow me down. Here’s what I’ve learned.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why WezTerm over everything else
&lt;/h3&gt;

&lt;p&gt;The terminal landscape is crowded. iTerm2, Alacritty, Kitty, Ghostty — all solid. I landed on WezTerm for a few specific reasons.&lt;/p&gt;

&lt;p&gt;It’s configured in Lua. That sounds minor but it matters: the config file is a real programming language, not an INI file with magic strings. You can write functions, conditionals, and abstractions. Here’s a minimal starting point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;wezterm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'wezterm'&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wezterm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config_builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;font&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wezterm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'FiraCode Nerd Font'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Regular'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;font_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;color_scheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Tokyo Night'&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enable_tab_bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hide_tab_bar_if_only_one_tab&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;window_padding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bottom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WezTerm also has a built-in multiplexer, and I use it heavily. Tab management is handled entirely through keyboard shortcuts — opening, closing, and switching tabs without touching the mouse. Each tab is a context: a project, a running process, a scratch environment. When I’m inside Neovim, I hand off navigation entirely to its internal buffer management and fuzzy search tooling. The two layers stay clean and don’t fight each other.&lt;/p&gt;

&lt;p&gt;GPU rendering means it stays fast even with large outputs. And it works identically on macOS and Linux, which matters when you’re working across machines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Neovim over VS Code
&lt;/h3&gt;

&lt;p&gt;This is the more loaded question, and I want to be honest about it: Neovim is not better than VS Code in every way. The plugin ecosystem is more fragmented, setup takes real time, and new contributors to a project can’t just open your config and understand it immediately.&lt;/p&gt;

&lt;p&gt;What Neovim does better: speed, keyboard-centricity, and composability. Once you internalize modal editing — the idea that you’re either navigating or editing, not both simultaneously — the efficiency difference is real. Motions like ciw (change inner word), va{ (select around braces), or % (jump to matching bracket) stop being things you remember and start being things you reach for without thinking.&lt;/p&gt;

&lt;p&gt;The LSP support in modern Neovim is excellent. With mason.nvim handling server installation and nvim-lspconfig wiring them up, you get the same language intelligence VS Code has — autocompletion, go-to-definition, inline diagnostics, rename across files — without a separate application running.&lt;/p&gt;

&lt;h3&gt;
  
  
  The plugin stack that actually matters
&lt;/h3&gt;

&lt;p&gt;There are a thousand Neovim plugin lists. Most include too much. Here’s what I actually use daily:&lt;/p&gt;

&lt;p&gt;lazy.nvim — plugin manager. Lazy-loads plugins so startup stays fast.&lt;/p&gt;

&lt;p&gt;telescope.nvim — fuzzy finder for files, grep, LSP symbols, git history. This is the single highest-leverage plugin in the ecosystem. ff to find a file and fg to grep across the project replace most file tree navigation.&lt;/p&gt;

&lt;p&gt;nvim-treesitter — syntax highlighting and code understanding via AST parsing rather than regex. Makes highlighting accurate and enables smart text objects.&lt;/p&gt;

&lt;p&gt;nvim-cmp — completion engine. Pulls from LSP, snippets, buffer words. Feels like IDE completion once configured.&lt;/p&gt;

&lt;p&gt;gitsigns.nvim — inline git blame, hunk staging, diff previews in the gutter. The kind of thing you don't know you need until you have it.&lt;/p&gt;

&lt;p&gt;conform.nvim — formatting on save, language-aware. Runs gofmt on Go files, prettier on TypeScript, without you thinking about it.&lt;/p&gt;

&lt;p&gt;A minimal plugin setup with these six gets you 90% of the way to a full IDE experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  How WezTerm and Neovim work together
&lt;/h3&gt;

&lt;p&gt;The short version: WezTerm is the container, Neovim is the environment. WezTerm handles font rendering, color, and tab management. Neovim handles editing, navigation, and LSP.&lt;/p&gt;

&lt;p&gt;I use WezTerm’s tab system for context switching — one tab per project or major task — and Neovim’s buffer and split system for everything within a project. The combination keeps me in the keyboard almost entirely. A typical navigation sequence: ff to jump to a file, gd to go to a definition,  to jump back, fg to grep for a usage. No mouse involved.&lt;/p&gt;

&lt;p&gt;Terminal commands stay in a dedicated WezTerm tab rather than Neovim’s built-in terminal, which keeps the separation clean.&lt;/p&gt;

&lt;h3&gt;
  
  
  The learning curve is real, and that’s fine
&lt;/h3&gt;

&lt;p&gt;I’m not going to undersell this. Getting productive in Neovim takes weeks, not hours. Vim motions have to become muscle memory, and that only happens through deliberate repetition — not reading about them.&lt;/p&gt;

&lt;p&gt;The path that worked for me: I started with VS Code in Vim mode. Not Neovim, not a terminal editor — just the Vim keybindings extension in an environment I already knew. That removed the “where is everything” friction and let me focus entirely on building motion muscle memory. I also spent time with &lt;a href="https://vim-adventures.com/" rel="noopener noreferrer"&gt;Vim Adventures&lt;/a&gt;, a browser game that teaches Vim navigation through actual gameplay. It sounds gimmicky. It works.&lt;/p&gt;

&lt;p&gt;By the time I moved to Neovim full-time, the motions were already there. The transition became about configuration and workflow, not simultaneously learning a new editor and a new input model.&lt;/p&gt;

&lt;p&gt;The other thing that helped: don’t try to replicate your VS Code setup in Neovim. Start with less than you think you need. Add things when you feel a specific friction, not because a blog post said you should. The instinct to add plugins until it feels familiar is how you end up with a config you don’t understand.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is it worth it?
&lt;/h3&gt;

&lt;p&gt;For me, yes — clearly. The speed, the composability, the satisfaction of a workflow that fits exactly how I think. But the honest answer is: it depends on what you optimize for. If you want to minimize setup time and maximize immediate productivity, VS Code wins. If you want an environment you can tune to exactly how your brain works and that will feel faster every month you use it, Neovim is worth the investment.&lt;/p&gt;

&lt;p&gt;At M²S² Engineering Group, the tools are always in service of the work — not the other way around. If you’re thinking through your team’s developer experience or how tooling choices affect velocity, &lt;a href="http://localhost:4200/contact" rel="noopener noreferrer"&gt;let’s talk&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>vim</category>
      <category>wezterm</category>
      <category>tooling</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Building a Serverless REST API with Go and AWS Lambda</title>
      <dc:creator>Michael Masterson</dc:creator>
      <pubDate>Tue, 21 Apr 2026 17:20:23 +0000</pubDate>
      <link>https://forem.com/mgmaster24/building-a-serverless-rest-api-with-go-and-aws-lambda-30ce</link>
      <guid>https://forem.com/mgmaster24/building-a-serverless-rest-api-with-go-and-aws-lambda-30ce</guid>
      <description>&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.amazonaws.com%2Fuploads%2Farticles%2F42nazjgboohm6x7jxcw6.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.amazonaws.com%2Fuploads%2Farticles%2F42nazjgboohm6x7jxcw6.png" alt="go-serverless" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;November 11, 2025&lt;/p&gt;

&lt;p&gt;I’ve built Go Lambda APIs a few different ways over the years. Some of those experiments are in production right now — including the API that powers this site. What follows is the pattern I’ve landed on: clean, minimal, and straightforward to operate.&lt;/p&gt;

&lt;p&gt;The short version: Go is one of the best Lambda runtimes available. Fast cold starts, a single static binary, a strong standard library, and a compiler that catches whole categories of runtime errors before they reach production. If you’re starting a new serverless API and aren’t already committed to another language, this is the stack I’d reach for.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Go specifically
&lt;/h3&gt;

&lt;p&gt;Lambda cold starts are real. A Go function compiled to a Linux arm64 binary typically initializes in under 100ms — often under 50ms. Compare that to Node.js or Python runtimes that need to load an interpreter and dependencies before your first line of code runs. For a public-facing API, that difference matters.&lt;/p&gt;

&lt;p&gt;The other thing Go gives you is a single self-contained binary. No node_modules, no virtual environments, no dependency resolution at deploy time. You compile locally, upload a zip file, and the Lambda runtime executes it directly. That simplicity pays dividends when you're debugging at 2am.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project structure
&lt;/h3&gt;

&lt;p&gt;Here’s how I structure a Go Lambda project in a monorepo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apps/api/
├── contact/
│ └── main.go # POST /contact handler
├── dashboard/
│ └── main.go # Auth-gated routes
└── shared/
    ├── models.go # DynamoDB item structs
    ├── dynamo.go # Query/put helpers
    ├── mailer.go # SES wrapper
    └── util.go # ID generation, CORS headers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each Lambda function gets its own directory with a main.go. Common code lives in shared/ as an internal package. This avoids the mistake of deploying a massive monolith Lambda that does everything — instead, each function has a focused responsibility and independent deploy surface.&lt;/p&gt;

&lt;p&gt;The shared package is where you put things that would otherwise be copy-pasted: DynamoDB helpers, the SES wrapper, CORS headers, ID generation. Keep it lean. If something is only used in one function, it stays in that function's package.&lt;/p&gt;

&lt;h3&gt;
  
  
  The handler pattern
&lt;/h3&gt;

&lt;p&gt;Every Lambda handler has the same shape: receive an events.APIGatewayV2HTTPRequest, return an events.APIGatewayV2HTTPResponse. I put all routing inside a single handler function using a switch on method + path:&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="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;APIGatewayV2HTTPRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;APIGatewayV2HTTPResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CORSHeaders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allowedOrigin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"GET, POST, OPTIONS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Content-Type, Authorization"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"OPTIONS"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;APIGatewayV2HTTPResponse&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;204&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"POST"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"/contact"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;handleContact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"GET"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"/health"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;APIGatewayV2HTTPResponse&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;`{"ok":true}`&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;errResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"not found"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is simpler than it looks. You don’t need a router library for a Lambda that handles 3–5 routes. The switch is readable, the cases are explicit, and there’s no magic. If a function grows beyond ~10 routes, that’s a signal it should be split.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reading the JWT for auth-gated routes
&lt;/h3&gt;

&lt;p&gt;API Gateway v2 with a Cognito JWT authorizer makes auth simple. The claims are injected into the request context — no token validation code needed in your Lambda:&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="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;jwtEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;APIGatewayV2HTTPRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;string&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;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Authorizer&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Authorizer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JWT&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Claims&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;APIGatewayV2HTTPRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&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;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Authorizer&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Authorizer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JWT&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Claims&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"cognito:groups"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Cognito authorizer rejects requests with invalid or expired tokens before they reach your code. Your handler only runs for requests that passed auth — so a missing or empty email is a programming error, not a security gap.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building and deploying with CDK
&lt;/h3&gt;

&lt;p&gt;The build step is straightforward. For each Lambda, you cross-compile to Linux arm64 and zip the binary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;linux &lt;span class="nv"&gt;GOARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arm64 go build &lt;span class="nt"&gt;-o&lt;/span&gt; bootstrap ./apps/api/contact/
zip contact.zip bootstrap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CDK stack in Go defines the function pointing at that zip:&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;contactFn&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;awslambda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jsii&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ContactFn"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;awslambda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FunctionProps&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;awslambda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime_PROVIDED_AL2023&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;Handler&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;jsii&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"bootstrap"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;awslambda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Code_FromAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsii&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"../../apps/api/contact.zip"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"TABLE_NAME"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;jsii&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="s"&gt;"FROM_EMAIL"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;jsii&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fromEmail&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s"&gt;"ALLOWED_ORIGIN"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;jsii&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allowedOrigin&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wire the function to API Gateway, add a Cognito JWT authorizer for protected routes, and deploy. The whole stack — Lambda, API Gateway, DynamoDB table, IAM roles — comes up in a single cdk deploy.&lt;/p&gt;

&lt;h3&gt;
  
  
  The gotchas
&lt;/h3&gt;

&lt;p&gt;A few things that aren’t obvious until you hit them:&lt;/p&gt;

&lt;p&gt;The binary must be named bootstrap. The provided.al2023 runtime looks for a file named exactly bootstrap in the zip. Name it anything else and you'll get a cryptic runtime error on first invocation.&lt;/p&gt;

&lt;p&gt;CORS preflight has to be handled explicitly. API Gateway v2 can handle CORS for you, or your Lambda can. Do one or the other — not both. If your Lambda returns CORS headers and the gateway also injects them, you’ll get duplicate headers and browsers will reject the response.&lt;/p&gt;

&lt;p&gt;Cold start is not your P99. Cold starts only happen when a new execution environment is provisioned. Under steady traffic, most requests hit warm instances. Don’t optimize for cold start at the cost of readability — it’s rarely the bottleneck you think it is.&lt;/p&gt;

&lt;p&gt;DynamoDB errors are usually IAM. If your Lambda can’t read or write to DynamoDB, check the execution role permissions before assuming your code is wrong. The AWS SDK swallows permission errors in ways that make them look like connection issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is it worth it?
&lt;/h3&gt;

&lt;p&gt;For the right workload — yes, absolutely. A Go Lambda API with DynamoDB behind API Gateway has essentially zero operational overhead. No servers to patch, no auto-scaling groups to tune, no capacity planning for moderate traffic. You pay for what you use, and Go’s efficiency means you use very little.&lt;/p&gt;

&lt;p&gt;Where it breaks down: workloads that need persistent connections (WebSockets, long-running jobs), very high concurrency with unpredictable bursts (Lambda has account-level concurrency limits), or anything that genuinely benefits from a stateful server process. Know the constraints going in and it’s a powerful tool.&lt;/p&gt;

&lt;p&gt;At M²S² Engineering Group, we help teams navigate exactly these kinds of architecture decisions — whether that’s serverless, containers, or something in between. If you’re weighing your options or just want a second opinion, &lt;a href="https://m2s2.io/contact" rel="noopener noreferrer"&gt;let’s talk&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>go</category>
      <category>awslambda</category>
      <category>serverless</category>
      <category>aws</category>
    </item>
    <item>
      <title>Engineering Leadership: What Nobody Tells You Before You Take the Job</title>
      <dc:creator>Michael Masterson</dc:creator>
      <pubDate>Tue, 21 Apr 2026 12:23:29 +0000</pubDate>
      <link>https://forem.com/mgmaster24/engineering-leadership-what-nobody-tells-you-before-you-take-the-job-5f1b</link>
      <guid>https://forem.com/mgmaster24/engineering-leadership-what-nobody-tells-you-before-you-take-the-job-5f1b</guid>
      <description>&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.amazonaws.com%2Fuploads%2Farticles%2Fke2zvltmknvdckhohrwr.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.amazonaws.com%2Fuploads%2Farticles%2Fke2zvltmknvdckhohrwr.png" alt="el" width="800" height="534"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Engineering Leadership&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I didn’t plan to become an engineering manager. I was a senior engineer who cared deeply about how the team worked, and at some point that care turned into a title. What followed was a crash course in everything a technical background doesn’t prepare you for.&lt;/p&gt;

&lt;p&gt;I’ve led teams of many engineers (full-time, onshore, nearshore, and offshore) through greenfield builds, legacy migrations, platform rewrites, and ambiguous mandates with moving deadlines. These are the things I wish someone had told me before I started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your output is no longer code
&lt;/h2&gt;

&lt;p&gt;This sounds obvious. but it isn’t, really. Not until you truly feel it. Early in my first management role I kept finding myself drifting back to the IDE. It felt productive. It was familiar. It was also the wrong use of my time.&lt;/p&gt;

&lt;p&gt;As an IC, your output is what you ship. As a manager, your output is what your team ships. Those are different jobs. The sooner you internalize that, the sooner you start spending your time on the things that actually multiply the team’s output — clearing blockers, making decisions, setting direction, hiring well, and building the kind of environment where engineers can do their best work without constantly checking with you first.&lt;/p&gt;

&lt;p&gt;That last part — reducing your own necessity — is the hardest thing to learn and the most important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ambiguity is the job, not an obstacle to it
&lt;/h2&gt;

&lt;p&gt;Engineers are trained to solve well-defined problems. Management is largely the practice of operating in conditions where the problem isn’t well-defined, the requirements will change, and someone above you wants a date.&lt;/p&gt;

&lt;p&gt;I’ve been handed teams mid-rebuild with mandates to modernize platforms that had been accumulating technical debt for years. Scope changed constantly. Stakeholder priorities shifted week to week. There was no clear finish line. The job was to keep the team moving productively despite that, not to wait for clarity that was never coming.&lt;/p&gt;

&lt;p&gt;The engineers who struggled most in that environment were the ones waiting for permission to move. The ones who thrived understood that forward motion with imperfect information beats paralysis while waiting for a perfect spec. Your job as a leader is to model that and to create enough psychological safety that your team feels the same way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trust is built in small moments, not big ones
&lt;/h2&gt;

&lt;p&gt;I’ve seen managers try to build team trust through all-hands talks, offsites, and team-building events. Those things aren’t bad, but they’re not where trust actually gets built.&lt;/p&gt;

&lt;p&gt;Trust is built when you fight for someone’s promotion and they find out. When you take the blame in a postmortem instead of pointing at the engineer who made the call. When you remember what someone said in a 1:1 three weeks ago and follow up on it without being asked. When you give direct feedback early instead of letting something fester into a performance conversation six months later.&lt;/p&gt;

&lt;p&gt;None of those are dramatic moments. They’re small, repeated actions over time. That’s what trust is made of.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical direction has to be set, not consensus-built
&lt;/h2&gt;

&lt;p&gt;There’s a version of technical leadership where every architecture decision goes through the whole team, everyone has a voice, and nothing gets decided until there’s buy-in. I understand the instinct. It backfires constantly.&lt;/p&gt;

&lt;p&gt;Teams move fast when they have clear guardrails — approved patterns, defined standards, explicit rules about what’s off-limits — and then autonomy inside those guardrails. What they can’t do is move fast when every decision requires a committee.&lt;/p&gt;

&lt;p&gt;I’ve defined frontend and backend standards that became the baseline for all platform development across teams. Engineers didn’t need to ask whether to use a certain pattern, the answer was already documented. That eliminated an enormous amount of friction. The team made more decisions independently, shipped more consistently, and had better code quality than any team I’d seen operate by pure committee.&lt;/p&gt;

&lt;p&gt;This doesn’t mean you don’t listen. It means you listen, decide, document, and then hold the line — revisiting when there’s real evidence the direction needs to change, not just because someone prefers a different approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hiring is the highest-leverage thing you do
&lt;/h2&gt;

&lt;p&gt;Everything else on this list (direction, trust, standards, culture) compounds if you have the right people and collapses if you don’t. One mis-hire in a key role can consume more of your time and team energy than any technical problem you’ll face.&lt;/p&gt;

&lt;p&gt;I’ve hired dozens of engineers across multiple organizations. The most effective change I made was introducing AI-assisted assessments that gave consistent, role-specific evaluations for every candidate. The result was a more defensible hiring bar, less prep time, and — critically — fewer late-stage surprises when someone’s actual ability didn’t match how they presented in an interview.&lt;/p&gt;

&lt;p&gt;What I look for beyond technical skill: how does someone operate when they don’t have enough information? Do they ask good questions or just wait? Can they disagree directly without being difficult? Have they shipped something that actually worked? Those signals matter more than algorithm fluency at a whiteboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing up is a skill, not a concession
&lt;/h2&gt;

&lt;p&gt;Early on I thought managing up — keeping stakeholders informed, shaping expectations, making the team’s work legible to leadership — was a distraction from real work. I was wrong.&lt;/p&gt;

&lt;p&gt;Leadership above you is making decisions with incomplete information. If you’re not providing that information, someone else is — and their version of events may not reflect what’s actually happening. Proactively communicating status, surfacing risks before they’re crises, and framing technical trade-offs in business terms isn’t politics. It’s protecting your team from decisions made in a vacuum.&lt;/p&gt;

&lt;p&gt;The cleaner your communication upward, the more autonomy you earn downward. That’s the deal.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thing that actually determines whether a team ships
&lt;/h2&gt;

&lt;p&gt;After years of this, I’ve come to believe that the single biggest predictor of team output isn’t process, tooling, or even talent. It’s psychological safety — the degree to which engineers feel safe raising problems, asking questions, and being wrong in front of each other.&lt;/p&gt;

&lt;p&gt;Teams with high safety surface bugs earlier. They escalate sooner. They try things that might not work. They tell you when a deadline isn’t realistic instead of staying quiet and missing it. Teams without safety hide problems until they’re catastrophic, defer hard conversations, and optimize for looking good over doing good work.&lt;/p&gt;

&lt;p&gt;You create it by being honest about what you don’t know. By treating mistakes as information rather than failures. By rewarding the person who raised a concern early over the person who stayed quiet and got lucky. It takes a long time to build and almost no time to destroy.&lt;/p&gt;

&lt;p&gt;At M²S² Engineering Group, engineering leadership is part of every engagement — not an afterthought. Whether you need an embedded partner or someone to help set direction, click the link and &lt;a href="http://m2s2.io/contact" rel="noopener noreferrer"&gt;let’s talk about what that looks like&lt;/a&gt;.&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.amazonaws.com%2Fuploads%2Farticles%2Fhs0oa9lxumo3xmreblww.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.amazonaws.com%2Fuploads%2Farticles%2Fhs0oa9lxumo3xmreblww.png" width="800" height="534"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;M2S2 Engineering Group&lt;/em&gt;&lt;/p&gt;

</description>
      <category>managementandleaders</category>
      <category>leadership</category>
      <category>softwareengineering</category>
      <category>engineeringmanagemen</category>
    </item>
  </channel>
</rss>
