<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michael Masterson</title>
    <description>The latest articles on DEV Community by Michael Masterson (@mgmaster24).</description>
    <link>https://dev.to/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>DEV Community: Michael Masterson</title>
      <link>https://dev.to/mgmaster24</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mgmaster24"/>
    <language>en</language>
    <item>
      <title>The Truth About Serverless APIs Nobody Mentions</title>
      <dc:creator>Michael Masterson</dc:creator>
      <pubDate>Thu, 14 May 2026 19:11:23 +0000</pubDate>
      <link>https://dev.to/mgmaster24/the-truth-about-serverless-apis-nobody-mentions-kla</link>
      <guid>https://dev.to/mgmaster24/the-truth-about-serverless-apis-nobody-mentions-kla</guid>
      <description>&lt;p&gt;I've noticed serverless conversations tend to split into two camps:&lt;/p&gt;

&lt;p&gt;People who think it solves everything, and people who think it's a mistake.&lt;/p&gt;

&lt;p&gt;The reality is much more useful than either extreme.&lt;/p&gt;

&lt;p&gt;Everyone who sells you on serverless leads with the same three points: no servers to manage, automatic scaling, and you only pay for what you use.&lt;/p&gt;

&lt;p&gt;All of that is technically true.&lt;/p&gt;

&lt;p&gt;None of it is the whole story.&lt;/p&gt;

&lt;p&gt;I've shipped serverless APIs in production — Go Lambdas behind API Gateway, the kind that power real user-facing products. I'm not here to talk you out of it. For the right workload, it's genuinely excellent.&lt;/p&gt;

&lt;p&gt;But the gaps between the pitch and the reality are specific, and knowing them in advance changes the decisions you make early, when they're still cheap to change.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cold starts are real — just not how you think
&lt;/h2&gt;

&lt;p&gt;Lambda cold starts happen when AWS needs to provision a new execution environment for your function. The runtime boots, your init code runs, and then the handler executes.&lt;/p&gt;

&lt;p&gt;For a Go function on arm64, that's typically 50–150ms total. For Node.js or Python with heavy dependencies, it can be 500ms–2 seconds.&lt;/p&gt;

&lt;p&gt;The part nobody leads with: cold starts are probabilistic, not guaranteed.&lt;/p&gt;

&lt;p&gt;Under steady traffic, most invocations hit warm instances. You'll rarely see them in your P50 or even P95. Where they show up is P99 and beyond — the tail latency your best users experience right after a traffic spike, or the first request after a quiet weekend.&lt;/p&gt;

&lt;p&gt;The practical advice: profile in production with real traffic before you add Provisioned Concurrency. It costs money and adds complexity.&lt;/p&gt;

&lt;p&gt;For most APIs, the smarter move is optimizing your init code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;lazy-load dependencies&lt;/li&gt;
&lt;li&gt;keep your binary small&lt;/li&gt;
&lt;li&gt;initialize only what the first request actually needs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fix the actual problem before reaching for the expensive solution.&lt;/p&gt;




&lt;h2&gt;
  
  
  The billing math will surprise you
&lt;/h2&gt;

&lt;p&gt;Lambda pricing is simple on paper: you pay per request and per GB-second of execution time.&lt;/p&gt;

&lt;p&gt;At low traffic, it's nearly free.&lt;/p&gt;

&lt;p&gt;The surprise comes at scale — or in specific usage patterns.&lt;/p&gt;

&lt;p&gt;The scenario that bites teams most often: a Lambda that does meaningful work per invocation. Parsing a large payload, doing in-memory transformation, making multiple downstream calls.&lt;/p&gt;

&lt;p&gt;These functions run longer, use more memory, and the cost per invocation adds up faster than the pricing page suggests.&lt;/p&gt;

&lt;p&gt;A few numbers from real workloads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A lightweight REST Lambda (Go, 128MB, 20ms avg): ~$0.40 per million requests&lt;/li&gt;
&lt;li&gt;A heavier processing Lambda (Node, 512MB, 800ms avg): ~$8.00 per million requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That 20× difference in cost per million requests adds up quickly when you have real traffic.&lt;/p&gt;

&lt;p&gt;It's still cheap compared to running servers — but the assumption that serverless is always the low-cost option needs to be verified, not assumed.&lt;/p&gt;

&lt;p&gt;API Gateway pricing is also frequently missed. At $3.50 per million requests for HTTP APIs, a high-traffic public endpoint can make Gateway cost more than the Lambda it fronts.&lt;/p&gt;

&lt;p&gt;Know the full cost model before you commit.&lt;/p&gt;




&lt;h2&gt;
  
  
  Debugging is a different skill
&lt;/h2&gt;

&lt;p&gt;Local development for Lambda is awkward.&lt;/p&gt;

&lt;p&gt;You can simulate the execution environment with tools like AWS SAM or custom test harnesses, but you're never truly running the same thing that runs in production.&lt;/p&gt;

&lt;p&gt;The gap shows up in exactly the moments you can least afford it.&lt;/p&gt;

&lt;p&gt;Observability requires deliberate setup.&lt;/p&gt;

&lt;p&gt;A Lambda that exits silently leaves no trace unless you've instrumented it. CloudWatch Logs are the floor, not the ceiling — you need structured logging, correlation IDs, and ideally X-Ray tracing before you're in production, not after something breaks.&lt;/p&gt;

&lt;p&gt;The failure modes are also harder to reproduce.&lt;/p&gt;

&lt;p&gt;A Cognito authorizer rejecting a token before your code runs looks different from your code returning a 401. A DynamoDB throttle on a hot partition looks different from a Lambda timeout.&lt;/p&gt;

&lt;p&gt;Getting fast at debugging serverless systems means learning the AWS console deeply:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CloudWatch Insights queries&lt;/li&gt;
&lt;li&gt;X-Ray service maps&lt;/li&gt;
&lt;li&gt;reading IAM policy errors without a stack trace to guide you&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Statelessness is a constraint, not just a pattern
&lt;/h2&gt;

&lt;p&gt;Every Lambda invocation starts with a clean slate.&lt;/p&gt;

&lt;p&gt;That's the feature — it's what makes horizontal scaling effortless.&lt;/p&gt;

&lt;p&gt;It's also the constraint that eliminates entire categories of architectural approaches.&lt;/p&gt;

&lt;p&gt;No in-memory caches that persist across requests. No background jobs that run between invocations. No WebSocket connections that stay open. No files written to disk that the next request can read.&lt;/p&gt;

&lt;p&gt;If your application assumes any of those things, serverless isn't wrong — but it's not your foundation. It's a piece of a larger architecture that needs something else alongside it.&lt;/p&gt;

&lt;p&gt;This comes up most often when teams decide mid-project that they suddenly need a feature requiring persistent state.&lt;/p&gt;

&lt;p&gt;Adding ElastiCache or SQS to a serverless architecture is straightforward — but it's a different architecture than what you started with, and the integration work is non-trivial.&lt;/p&gt;

&lt;p&gt;The most dangerous part is that many of these problems don't show up until the system already matters.&lt;/p&gt;




&lt;h2&gt;
  
  
  Vendor lock-in is real, and that's okay
&lt;/h2&gt;

&lt;p&gt;A Lambda function isn't a Docker container.&lt;/p&gt;

&lt;p&gt;It doesn't run the same way everywhere else.&lt;/p&gt;

&lt;p&gt;Your Go binary targeting &lt;code&gt;provided.al2023&lt;/code&gt; on arm64 can technically run in other environments, but your API Gateway routes, Cognito authorizers, DynamoDB single-table design, and SES integration are AWS-specific throughout.&lt;/p&gt;

&lt;p&gt;I'm not arguing against it — I use all of those services in production.&lt;/p&gt;

&lt;p&gt;But it's worth being honest:&lt;/p&gt;

&lt;p&gt;If you go deep on serverless AWS, migrating later means a substantial rewrite.&lt;/p&gt;

&lt;p&gt;Make that decision deliberately, not by default.&lt;/p&gt;




&lt;h2&gt;
  
  
  When not to use it
&lt;/h2&gt;

&lt;p&gt;Serverless is the wrong tool when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have &lt;strong&gt;persistent connections&lt;/strong&gt; — WebSocket servers, gRPC streams, database connection pools&lt;/li&gt;
&lt;li&gt;You have &lt;strong&gt;long-running jobs&lt;/strong&gt; — Lambda has a 15-minute max timeout&lt;/li&gt;
&lt;li&gt;You have &lt;strong&gt;very high, sustained concurrency&lt;/strong&gt; — account-level concurrency limits can affect multiple services simultaneously&lt;/li&gt;
&lt;li&gt;You need &lt;strong&gt;low and predictable latency&lt;/strong&gt; at P99 — cold-start tail latency is difficult to eliminate completely&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  When it's the right call
&lt;/h2&gt;

&lt;p&gt;Event-driven workloads.&lt;/p&gt;

&lt;p&gt;APIs with bursty or unpredictable traffic.&lt;/p&gt;

&lt;p&gt;Background processing triggered by S3 uploads, SQS messages, or DynamoDB streams.&lt;/p&gt;

&lt;p&gt;Any workload where you don't want to manage capacity and the per-request cost model fits your traffic profile.&lt;/p&gt;

&lt;p&gt;The API powering this site is Go Lambdas behind API Gateway.&lt;/p&gt;

&lt;p&gt;Contact form submissions, resume requests, visitor analytics, admin dashboard — all of it.&lt;/p&gt;

&lt;p&gt;It handles the traffic I need, costs less than $1/month to run, and I've never paged for a server that went down.&lt;/p&gt;

&lt;p&gt;For that workload, it's the right call.&lt;/p&gt;




&lt;p&gt;The key is understanding what you're actually trading.&lt;/p&gt;

&lt;p&gt;Serverless isn't simpler than traditional infrastructure.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It's differently complex.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The operational burden shifts from server management to observability, IAM, and distributed system debugging.&lt;/p&gt;

&lt;p&gt;That trade is absolutely worth it in the right context.&lt;/p&gt;

&lt;p&gt;Go in with your eyes open, and it's one of the most productive architectures available.&lt;/p&gt;

&lt;p&gt;The right architecture should help your team maintain momentum as the system grows — not create friction six months later.&lt;/p&gt;

&lt;p&gt;At M²S² Engineering Group, we help teams make these decisions before they become expensive to unwind.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://m2s2.io/blog/serverless-aws-nobody-mentions" rel="noopener noreferrer"&gt;m2s2.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>lambda</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Structure Creates Power: Lessons from Wing Chun and Engineering</title>
      <dc:creator>Michael Masterson</dc:creator>
      <pubDate>Fri, 08 May 2026 20:14:02 +0000</pubDate>
      <link>https://dev.to/mgmaster24/structure-creates-power-lessons-from-wing-chun-and-engineering-4k5k</link>
      <guid>https://dev.to/mgmaster24/structure-creates-power-lessons-from-wing-chun-and-engineering-4k5k</guid>
      <description>&lt;p&gt;I recently started training Wing Chun.&lt;/p&gt;

&lt;p&gt;At first glance, it doesn't look like what most people expect from martial arts. There's no flash. No wasted motion. No emphasis on brute force. Everything is controlled, direct, and intentional.&lt;/p&gt;

&lt;p&gt;When I first walked into training, I expected to learn techniques—strikes, blocks, sequences.&lt;/p&gt;

&lt;p&gt;Instead, we spent time on something much simpler.&lt;/p&gt;

&lt;p&gt;Stance. Balance. Positioning.&lt;/p&gt;

&lt;p&gt;It wasn't exciting. It didn't feel like progress. But it quickly became clear that everything depended on it.&lt;/p&gt;

&lt;p&gt;In Wing Chun, there's a core idea that shows up in everything you do:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Structure creates power.&lt;br&gt;&lt;br&gt;
Economy of motion creates speed.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The more I train, the more I realize how closely this maps to building software.&lt;/p&gt;




&lt;p&gt;Early on, speed feels easy.&lt;/p&gt;

&lt;p&gt;You're building something new. Writing code, shipping features, making decisions quickly. It feels like momentum.&lt;/p&gt;

&lt;p&gt;And in a way, it is—at least at the start.&lt;/p&gt;

&lt;p&gt;But over time, something changes.&lt;/p&gt;

&lt;p&gt;Small decisions begin to stack. Patterns drift. The system becomes harder to reason about. Bugs start showing up in places that used to feel simple. Every new change introduces a little more friction than the last.&lt;/p&gt;

&lt;p&gt;You're still moving, but not forward.&lt;/p&gt;

&lt;p&gt;You're spending more time fixing than building.&lt;/p&gt;




&lt;p&gt;In Wing Chun, that breakdown is familiar.&lt;/p&gt;

&lt;p&gt;If your structure isn't right, everything becomes harder under pressure. Movements get sloppy. Reactions slow down. Power disappears.&lt;/p&gt;

&lt;p&gt;You can try to compensate with effort—but it doesn't hold up.&lt;/p&gt;

&lt;p&gt;That's why so much time is spent on foundation.&lt;/p&gt;

&lt;p&gt;Before anything complex, you focus on the basics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;where your weight sits
&lt;/li&gt;
&lt;li&gt;how your body is aligned
&lt;/li&gt;
&lt;li&gt;how you connect movement from one position to the next
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's not flashy. But it's what allows everything else to work.&lt;/p&gt;




&lt;p&gt;Software isn't that different.&lt;/p&gt;

&lt;p&gt;It's tempting to skip the foundation early on. To move quickly, defer decisions, and assume you'll clean things up later.&lt;/p&gt;

&lt;p&gt;And for a while, that works.&lt;/p&gt;

&lt;p&gt;But without a solid foundation, systems become fragile. Small changes carry risk. Complexity builds faster than expected. Eventually, the speed you thought you had starts working against you.&lt;/p&gt;




&lt;p&gt;What's interesting is that Wing Chun isn't about doing more.&lt;/p&gt;

&lt;p&gt;It's about doing less—but doing it well.&lt;/p&gt;

&lt;p&gt;Every movement has a purpose. There's no extra motion. No wasted energy. If something doesn't directly contribute to the outcome, it's removed.&lt;/p&gt;

&lt;p&gt;That idea carries over into engineering more than most people expect.&lt;/p&gt;

&lt;p&gt;The best systems aren't the ones with the most flexibility or the most features. They're the ones where each piece has a clear role, and everything works together in a way that feels intentional.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Simple systems move faster—not because they do more, but because they do less with clarity.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Once the foundation is in place, something shifts.&lt;/p&gt;

&lt;p&gt;In Wing Chun, movements become more consistent. Reactions become faster. Techniques that felt awkward start to flow naturally. You're not thinking as much—you're responding.&lt;/p&gt;

&lt;p&gt;More complex combinations become possible, not because you've added more, but because everything underneath them is stable.&lt;/p&gt;




&lt;p&gt;That's what a good foundation does in software.&lt;/p&gt;

&lt;p&gt;It doesn't slow you down. It removes friction.&lt;/p&gt;

&lt;p&gt;It gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;consistent patterns
&lt;/li&gt;
&lt;li&gt;clear boundaries
&lt;/li&gt;
&lt;li&gt;confidence in making changes
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From there, you can move faster—not just once, but repeatedly.&lt;/p&gt;

&lt;p&gt;You can build on top of what you've already created without constantly reworking it.&lt;/p&gt;




&lt;p&gt;This is where the idea of &lt;strong&gt;Engineering Momentum&lt;/strong&gt; really starts to make sense.&lt;/p&gt;

&lt;p&gt;Most teams don't struggle because they lack the ability to build.&lt;/p&gt;

&lt;p&gt;They struggle because the systems they build don't support continued progress.&lt;/p&gt;

&lt;p&gt;Speed shows up early. But it doesn't last.&lt;/p&gt;

&lt;p&gt;Bugs increase. Changes get harder. Teams spend more time stabilizing than creating.&lt;/p&gt;

&lt;p&gt;Momentum breaks.&lt;/p&gt;




&lt;p&gt;The teams that maintain momentum tend to do something different.&lt;/p&gt;

&lt;p&gt;They don't avoid speed—but they don't chase it blindly either.&lt;/p&gt;

&lt;p&gt;They make a few key decisions early:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;establish structure
&lt;/li&gt;
&lt;li&gt;define patterns
&lt;/li&gt;
&lt;li&gt;create a foundation that can support growth
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not over-engineering. Not unnecessary complexity.&lt;/p&gt;

&lt;p&gt;Just enough structure to keep things moving forward.&lt;/p&gt;




&lt;p&gt;Wing Chun isn't about brute force.&lt;/p&gt;

&lt;p&gt;It's about alignment, structure, and efficiency.&lt;/p&gt;

&lt;p&gt;The power doesn't come from doing more—it comes from doing the right things, in the right way, with the right foundation underneath.&lt;/p&gt;




&lt;p&gt;The same is true in software.&lt;/p&gt;

&lt;p&gt;You don't need the most complex system.&lt;br&gt;&lt;br&gt;
You don't need the most flexible architecture.&lt;br&gt;&lt;br&gt;
You don't need to solve every problem up front.&lt;/p&gt;

&lt;p&gt;You need a foundation that allows you to keep moving.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Structure creates power.&lt;br&gt;&lt;br&gt;
Simplicity creates speed.&lt;br&gt;&lt;br&gt;
And the right foundation creates momentum.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://m2s2.io/blog/structure-creates-power-wing-chun-engineering" rel="noopener noreferrer"&gt;m2s2.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>engineering</category>
      <category>engineeringmomentum</category>
      <category>mindset</category>
      <category>architecture</category>
    </item>
    <item>
      <title>What I Do in the First 30 Days With a New Engineering Team</title>
      <dc:creator>Michael Masterson</dc:creator>
      <pubDate>Mon, 04 May 2026 02:57:19 +0000</pubDate>
      <link>https://dev.to/mgmaster24/what-i-do-in-the-first-30-days-with-a-new-engineering-team-374k</link>
      <guid>https://dev.to/mgmaster24/what-i-do-in-the-first-30-days-with-a-new-engineering-team-374k</guid>
      <description>&lt;p&gt;Most founders who bring in a fractional engineering leader are looking for the same thing: &lt;em&gt;engineering momentum&lt;/em&gt;. Not activity — engineers are almost always busy — but directed motion. Work that compounds toward something, where effort today makes tomorrow's effort easier rather than harder.&lt;/p&gt;

&lt;p&gt;When that's missing, the symptoms are familiar. Delivery has slowed. Cross-team communication feels like friction rather than collaboration. Something that should take a week takes a month, and nobody can quite explain why. The team seems capable, but the output doesn't reflect it.&lt;/p&gt;

&lt;p&gt;What's usually underneath that isn't a talent problem. It's a system problem. And untangling it — in a way that actually sticks — takes more than a fresh set of eyes. It takes experience reading the specific signals that tell you what's actually happening versus what the visible problems are pointing at.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Week 1: Build the map
&lt;/h2&gt;

&lt;p&gt;Before anything changes, I need to understand the system — and a software team is a system, not just a collection of people writing code. That means looking at the technical layer and the human layer simultaneously, because they almost always reflect each other.&lt;/p&gt;

&lt;p&gt;I meet every engineer individually in the first week. One at a time, without an agenda beyond understanding their experience. What are they working on? What's making it harder than it should be? If they could change one thing, what would it be?&lt;/p&gt;

&lt;p&gt;What surfaces in these conversations is irreplaceable. Engineers know where the complexity lives and why it got there. They know which decisions created the most downstream pain. They know who carries the most implicit knowledge and who is quietly stretched thin. They also know — and this comes up more than founders expect — when their team has drifted into operating in isolation. Processes that were never documented. Tooling built internally that solves problems nobody else in the organization knows they solved. Decisions made locally that have never been communicated upward or across to other teams.&lt;/p&gt;

&lt;p&gt;That last pattern is one of the most common and most costly things I encounter. Teams develop their own ways of working — their own rhythms, their own standards, their own internal language — and those ways of working never make it out of the team. What starts as autonomy quietly becomes a silo. And silos make organizational communication slow, expensive, and fragile.&lt;/p&gt;

&lt;p&gt;I also spend time in the code this week — reading commit history, tracing the architecture, understanding where complexity has clustered and why. A codebase reflects the culture that built it. The shape of the technical debt tells you something about how decisions were made and whether they were ever revisited. Undocumented patterns tell you what was understood but never written down. If you know how to read it, the codebase is one of the most honest documents in the organization.&lt;/p&gt;

&lt;p&gt;By the end of week one I have a working map — of the people, the technical landscape, and the relationship between the two.&lt;/p&gt;

&lt;h2&gt;
  
  
  Week 2: Find the real constraints
&lt;/h2&gt;

&lt;p&gt;Most teams that are underperforming aren't lacking talent. They're operating inside a set of structural constraints that create friction across nearly everything they do — and because the friction is everywhere, it feels like the natural cost of building software rather than something that can be addressed.&lt;/p&gt;

&lt;p&gt;This week is about finding what those constraints actually are, specifically, for this team and this organization. I start showing up more visibly — in code reviews, architecture discussions, planning sessions — not to direct them yet, but to see how the work actually flows in practice.&lt;/p&gt;

&lt;p&gt;The separation between symptoms and causes is where real diagnostic experience matters. Slow delivery, unclear priorities, duplicated effort, decisions that have to be relitigated every sprint — these are real, but they're downstream of something more specific. The skill isn't just knowing what patterns to look for. It's knowing which ones are causing the others, and in what sequence addressing them will create the most leverage.&lt;/p&gt;

&lt;p&gt;One of the patterns I look for explicitly is what engineers sometimes call "not invented here" syndrome — the tendency for teams to build or develop their own version of something rather than adopt what already exists, either within the organization or in the broader ecosystem. It comes from a good instinct: engineers want to understand and own what they're building. But left unchecked, it compounds into a much larger problem. Each team's internal tooling, processes, and conventions become a private language. Cross-team collaboration requires translation. Organizational knowledge stays trapped inside individual teams rather than flowing across them. What should be a shared foundation becomes a collection of parallel, incompatible approaches — and the cost shows up as exactly the kind of slow, grinding friction that makes it feel like the whole organization is moving through water.&lt;/p&gt;

&lt;p&gt;Addressing this isn't about standardizing everything into uniformity. It's about identifying what needs to be shared and building the bridges that let it be. That's a technical problem, an organizational problem, and a communication problem all at once — and untangling it requires understanding all three.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Team structure becomes concrete this week as well.&lt;/strong&gt; Not headcount, but organization. Does ownership match accountability? Are there engineers operating above or below their actual level? Are there gaps a new hire would fill, or gaps that restructuring existing roles would address just as well? Scaling a team doesn't always mean growing it. Sometimes the leverage is in removing the structural friction that's preventing the people already there from doing their best work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Week 3: Begin moving
&lt;/h2&gt;

&lt;p&gt;The trust built in the first two weeks has to translate into visible change, or it dissipates. By week three the team is watching — not skeptically, but expectantly. They shared things with you. They want to see what you do with it.&lt;/p&gt;

&lt;p&gt;I pick the highest-leverage change available and make it. Most often this is a process or communication change rather than a technical one — not because the technical work isn't important, but because process changes produce immediate, felt results. A clear definition of done eliminates the ongoing ambiguity about what "finished" means. A code review standard cuts the time spent waiting on feedback. A shared architecture decision record means that the decision made this week doesn't have to be made again in six months when the context has evaporated.&lt;/p&gt;

&lt;p&gt;On the organizational communication side, this is often when I start building the connective tissue that was missing — shared documentation standards, cross-team visibility into technical decisions, communication patterns that let teams stay loosely coupled rather than accidentally isolated. None of this is glamorous. All of it matters.&lt;/p&gt;

&lt;p&gt;The people conversations deepen this week as well. Role misalignments, skill gaps, or team dynamics that are creating drag get addressed directly — with honesty, with care, and with a clear picture of what a better outcome looks like. Engineers want to know where they stand. Most of the difficult conversations I've had with people on teams were hard because they'd been deferred, not because they were inherently bad news.&lt;/p&gt;

&lt;p&gt;If there are hiring needs, I start building the case now — not as a headcount request, but as a specific capability tied to a specific outcome. What can this team not do today that it needs to do next quarter, and is there someone already here who could grow into it, or does that capability need to come from outside?&lt;/p&gt;

&lt;h2&gt;
  
  
  Week 4: Establish the next horizon
&lt;/h2&gt;

&lt;p&gt;By week four the team should feel different — not because the fundamental work has changed, but because the structural friction around it has started to lift. Velocity isn't a metric. It's a quality of motion. A shared sense of direction and the confidence that comes from knowing what you're optimizing for.&lt;/p&gt;

&lt;p&gt;I bring a clear picture to the founder or leadership team: the team's actual strengths, what was creating the most drag and what has been done about it, where the remaining gaps are, and what the people and organizational plan looks like going forward. Not a deck of observations — a grounded account of the current state, built from real understanding of the codebase, the team, and the business context they're operating in.&lt;/p&gt;

&lt;p&gt;Then I define the next 60 days in terms the whole team can commit to. Real targets tied to actual capacity. Technical priorities sequenced against the team's current constraints rather than set against an idealized version of them. Organizational changes that are still in motion but have a clear destination.&lt;/p&gt;

&lt;p&gt;The goal of the first month isn't transformation. It's foundation — the kind that makes transformation possible. A team that understands what it's building and why, with the communication patterns and shared standards to move as a unit rather than a collection of individuals, is a team that can build what actually moves the business forward.&lt;/p&gt;

&lt;p&gt;That's engineering momentum. And it's what every decision in the first 30 days is aimed at.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this looks like from where you're sitting
&lt;/h2&gt;

&lt;p&gt;If you're a founder watching this unfold, the first week will feel deliberate rather than urgent. That's intentional. The quality of everything that comes after depends on the quality of the understanding built first — and rushing that understanding is where most well-intentioned interventions go wrong.&lt;/p&gt;

&lt;p&gt;By the end of week two you should have more clarity about what's actually happening in your engineering organization than you had before — not because I've handed you a summary, but because the right questions have a way of making things that were always true finally visible.&lt;/p&gt;

&lt;p&gt;By the end of week four you should see motion. Not a finished transformation, but a team moving with clearer direction, fewer structural obstacles, and a shared sense of what the next quarter is actually going to require of them.&lt;/p&gt;

&lt;p&gt;That's the foundation. Everything built on top of it is faster, sturdier, and more aligned with where the business is actually trying to go.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If your team is carrying more friction than it should — or you're not sure what's causing the slowdown — &lt;a href="https://dev.to/contact"&gt;let's talk about it&lt;/a&gt;. Engineering momentum is buildable. You just need to know where to start.&lt;/em&gt;&lt;/p&gt;




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

</description>
      <category>leadership</category>
      <category>consulting</category>
      <category>startup</category>
      <category>engineering</category>
    </item>
    <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://dev.to/mgmaster24/i-choose-angular-over-react-for-large-projects-heres-why-194b</link>
      <guid>https://dev.to/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://dev.to/mgmaster24/using-claude-to-move-faster-without-cutting-corners-3ii2</link>
      <guid>https://dev.to/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://dev.to/mgmaster24/modern-angular-architecture-from-ngmodules-to-standalone-components-3fla</link>
      <guid>https://dev.to/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://dev.to/mgmaster24/engineering-leadership-what-nobody-tells-you-before-you-take-the-job-3c59</link>
      <guid>https://dev.to/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>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://dev.to/mgmaster24/what-a-fractional-engineering-leader-actually-does-3jnp</link>
      <guid>https://dev.to/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://dev.to/mgmaster24/my-terminal-native-dev-setup-wezterm-and-neovim-21h</link>
      <guid>https://dev.to/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://dev.to/mgmaster24/building-a-serverless-rest-api-with-go-and-aws-lambda-30ce</link>
      <guid>https://dev.to/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://dev.to/mgmaster24/engineering-leadership-what-nobody-tells-you-before-you-take-the-job-5f1b</link>
      <guid>https://dev.to/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>
