[{"data":1,"prerenderedAt":309},["ShallowReactive",2],{"navigation":3,"/blog/why-writing-engineering-blogs-is-hard":141,"/blog/why-writing-engineering-blogs-is-hard-surround":298},[4,36,55,80,101,116,128],{"title":5,"path":6,"stem":7,"children":8,"icon":35},"ASP.NET Core","/aspnet-core","1.aspnet-core/1.index",[9,11,15,19,23,27,31],{"title":10,"path":6,"stem":7},"Cursor Pagination",{"title":12,"path":13,"stem":14},"Dapper - Micro ORM","/aspnet-core/dapper","1.aspnet-core/2.dapper",{"title":16,"path":17,"stem":18},"DbUp - Database Migrations","/aspnet-core/dbup","1.aspnet-core/3.dbup",{"title":20,"path":21,"stem":22},"Serilog - Structured Logging for .NET","/aspnet-core/serilog","1.aspnet-core/4.serilog",{"title":24,"path":25,"stem":26},"Class Inheritance in C#","/aspnet-core/inheritance","1.aspnet-core/5.inheritance",{"title":28,"path":29,"stem":30},"C# Collections","/aspnet-core/collections","1.aspnet-core/6.collections",{"title":32,"path":33,"stem":34},".NET gitignore Command","/aspnet-core/gitignore","1.aspnet-core/7.gitignore",false,{"title":37,"path":38,"stem":39,"children":40,"icon":35},"Oauth2.0 and OpenIdConnect","/oauth2andopenidconnect","10.oauth2andopenidconnect/1.index",[41,43,47,51],{"title":42,"path":38,"stem":39},"RFC 6749 - OAuth 2.0 Authorization Framework",{"title":44,"path":45,"stem":46},"Authentication (AuthN) vs Authorization (AuthZ)","/oauth2andopenidconnect/authnandauthz","10.oauth2andopenidconnect/2.authnandauthz",{"title":48,"path":49,"stem":50},"TOTP Authentication","/oauth2andopenidconnect/totp","10.oauth2andopenidconnect/3.totp",{"title":52,"path":53,"stem":54},"OAuth 2.0 Resource Indicators - RFC 8707","/oauth2andopenidconnect/oauth2resourceindicators","10.oauth2andopenidconnect/4.OAuth2ResourceIndicators",{"title":56,"path":57,"stem":58,"children":59,"page":35},"Azure","/azure","2.azure",[60,64,68,72,76],{"title":61,"path":62,"stem":63},"Azure Cost Management","/azure/azuremanagementandgovernance","2.azure/1.Azuremanagementandgovernance",{"title":65,"path":66,"stem":67},"Azure Policy","/azure/azurepolicy","2.azure/2.azurepolicy",{"title":69,"path":70,"stem":71},"Code Blocks","/azure/code-blocks","2.azure/3.code-blocks",{"title":73,"path":74,"stem":75},"Prose Components","/azure/prose-components","2.azure/4.prose-components",{"title":77,"path":78,"stem":79},"Images and Embeds","/azure/images-embeds","2.azure/5.images-embeds",{"title":81,"path":82,"stem":83,"children":84,"page":35},"Git","/git","3.git",[85,89,93,97],{"title":86,"path":87,"stem":88},"Git Rebase","/git/git-rebase","3.git/1.git-rebase",{"title":90,"path":91,"stem":92},"Git Stash","/git/git-stash","3.git/2.git-stash",{"title":94,"path":95,"stem":96},"SemVer","/git/semver","3.git/3.semver",{"title":98,"path":99,"stem":100},"Conventional Commits","/git/conventional-commits","3.git/4.conventional-commits",{"title":102,"path":103,"stem":104,"children":105,"icon":35},"Design Patterns","/design-patterns","7.design-patterns/1.index",[106,108,112],{"title":107,"path":103,"stem":104},"Introduction",{"title":109,"path":110,"stem":111},"Installation","/design-patterns/installation","7.design-patterns/2.installation",{"title":113,"path":114,"stem":115},"Usage","/design-patterns/usage","7.design-patterns/3.usage",{"title":117,"path":118,"stem":119,"children":120,"icon":35},"Software Principles","/principles","8.principles/1.index",[121,122,125],{"title":107,"path":118,"stem":119},{"title":109,"path":123,"stem":124},"/principles/installation","8.principles/2.installation",{"title":113,"path":126,"stem":127},"/principles/usage","8.principles/3.usage",{"title":129,"path":130,"stem":131,"children":132,"icon":35},"Software Architecture","/architecture","9.architecture/1.index",[133,135,138],{"title":134,"path":130,"stem":131},"CQRS Pattern",{"title":109,"path":136,"stem":137},"/architecture/installation","9.architecture/2.installation",{"title":113,"path":139,"stem":140},"/architecture/usage","9.architecture/3.usage",{"id":142,"title":143,"body":144,"date":287,"description":288,"extension":289,"featured":290,"meta":291,"navigation":290,"path":292,"readTime":293,"seo":294,"stem":295,"tag":296,"__hash__":297},"blog/blog/13.why-writing-engineering-blogs-is-hard.md","Why Writing Engineering Blogs Is Hard",{"type":145,"value":146,"toc":272},"minimark",[147,151,154,159,162,166,169,173,193,197,200,204,211,215,218,222,225,229,238,242,245,248,252],[148,149,150],"p",{},"The hardest habit to break when I write about engineering is the pull toward sounding absolute. It's so much easier to write \"this is how software should be built\" than it is to write \"given what I've seen, this is how I'd approach it.\" The first sentence closes the conversation. The second one leaves room for someone to push back, add context I didn't have, or just disagree — and that's the sentence I actually mean, every time.",[148,152,153],{},"It's worth being explicit about what that distinction actually means in practice, because it's easy to read past it. When I write about a topic, I'm describing what I'm doing or what I would do — not trying to move you toward my line of thought. Those are genuinely different goals. One is a report from where I'm standing. The other is an argument built to relocate you to where I'm standing, and that's not what I'm after. If you read something I've written and end up somewhere different from me, having actually thought it through, that's not a miss. That's the only outcome I was ever really hoping for.",[155,156,158],"h2",{"id":157},"i-wont-give-you-an-architecture-without-understanding-the-problem-first","I Won't Give You an Architecture Without Understanding the Problem First",[148,160,161],{},"If someone asked me how I'd build a Hospital Management System, I could talk for hours about Clean Architecture, DDD, CQRS, event sourcing, microservices, modular monoliths, vertical slices. None of that would be an answer yet. Before I'd commit to any of it, I'd want to know whether this is a rural clinic or a national referral hospital, how many concurrent users it needs to handle, whether it has to work offline, whether it integrates with labs or insurers, what the regulatory and audit requirements are, how often it's expected to change, and who's going to be the one maintaining it after I'm gone. Recommend an architecture before knowing those answers, and what I'm really giving you is an opinion wearing the costume of expertise.",[155,163,165],{"id":164},"what-i-write-are-preferences-not-standards","What I Write Are Preferences, Not Standards",[148,167,168],{},"When I write about architecture, security, or engineering practice, I'm not handing anyone a checklist to follow. I'm documenting how I'd approach a specific problem, shaped by the projects I've actually worked on, the incidents that taught me something the hard way, the systems that succeeded, the ones that didn't, and a fair amount of reading and arguing with other engineers along the way. Another experienced engineer looking at the same problem might land somewhere different. That's not a flaw in either of our reasoning — it's what happens when two people bring different constraints and different scars to the same question.",[155,170,172],{"id":171},"i-think-in-trade-offs-not-in-collected-patterns","I Think in Trade-offs, Not in Collected Patterns",[148,174,175,176,183,184,188,189,192],{},"Every choice I make optimizes for something at the expense of something else. A modular monolith trades independent scaling for simplicity and a smaller team's ability to reason about the whole system. Microservices trade that simplicity for independent deployment and organizational autonomy. Neither is better in the abstract — they're answers to different questions, and the same is true of every database, messaging system, authentication approach, and caching strategy I've ever picked. This is basically why I've come to like the discipline behind ",[177,178,182],"a",{"href":179,"rel":180},"http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions",[181],"nofollow","Architecture Decision Records",", the format Michael Nygard proposed back in 2011: not a record of the \"right\" answer, but of the specific context that made a particular trade-off the right call ",[185,186,187],"em",{},"then"," — which is also an honest admission that a different context might make a different call the right one ",[185,190,191],{},"now",". I've stopped thinking of my job as collecting the right patterns. It's choosing the right trade-off for the specific thing in front of me, and being able to say why.",[155,194,196],{"id":195},"i-try-not-to-mock-legacy-systems-because-i-might-have-built-them","I Try Not to Mock Legacy Systems, Because I Might Have Built Them",[148,198,199],{},"One thing that's genuinely changed how I think: every legacy system I've ever complained about was, at some point, someone's modern, considered decision — often made by an engineer just as experienced as I am, working with the tools, timelines, and knowledge available at that moment. Given the same constraints, I might have made exactly the same call. That's a humbling thing to sit with, and it's made it a lot harder for me to look at an old system and ask \"what were they thinking\" without also asking what I don't yet know about the decision I'm currently proud of.",[155,201,203],{"id":202},"when-people-ask-if-everyone-should-use-x-my-honest-answer-is-it-depends","When People Ask If Everyone Should Use X, My Honest Answer Is \"It Depends\"",[148,205,206,207,210],{},"I get some version of this question constantly — should every project use Clean Architecture, should everything be event-driven, should we always reach for CQRS. My answer is almost always \"it depends,\" and I've come to think that's not a dodge. A pattern exists because it solved a real problem for someone, somewhere. That's not the same as it solving my problem, here, now. The engineers I respect most are the ones who can tell me ",[185,208,209],{},"why"," a pattern exists before they'll tell me whether to use it.",[155,212,214],{"id":213},"nothing-i-write-today-is-meant-to-be-final","Nothing I Write Today Is Meant to Be Final",[148,216,217],{},"Part of why I resist writing anything as definitive guidance is that the target keeps moving. New vulnerabilities get discovered. Frameworks change. Business requirements shift under a system that was designed around yesterday's assumptions. Something I write with full conviction today might genuinely deserve revision in two years — not because I was wrong when I wrote it, but because the field kept evolving underneath the article the whole time. I'd rather admit that up front than pretend I've written something timeless.",[155,219,221],{"id":220},"my-perspective-comes-from-where-ive-actually-worked","My Perspective Comes From Where I've Actually Worked",[148,223,224],{},"An engineer who's spent years in banking systems is going to value different things than one who's spent years on gaming engines, hospital platforms, or embedded devices — and that's exactly as true of me as it is of anyone else. My opinions are downstream of my experience, not independent of it. That's the whole reason I say \"this is how I'd approach it\" instead of \"this is how everyone should approach it.\" The first sentence is honest about where the opinion came from. The second one pretends my specific path through this field is somehow universal.",[155,226,228],{"id":227},"id-rather-start-a-discussion-than-end-one","I'd Rather Start a Discussion Than End One",[148,230,231,232,237],{},"The engineering conversations I've learned the most from almost never ended in everyone agreeing. Someone challenges an assumption I hadn't examined. Someone else brings production experience that reframes the whole question. There's a half-joking version of this idea floating around under the name ",[177,233,236],{"href":234,"rel":235},"https://www.laws-of-software.com/laws/cunningham/",[181],"Cunningham's Law"," — that the fastest way to get a good answer isn't to ask a clean question, it's to state something slightly wrong and let people who know better correct it. I don't write with that as a strategy, but I recognize the shape of it in how these conversations actually go: what comes out the other side usually isn't a winner — it's a better version of the question we started with. That's what I actually want from anything I write: not agreement, but the kind of pushback that leaves both of us thinking more clearly than we started.",[155,239,241],{"id":240},"im-not-trying-to-be-right-im-trying-to-show-my-reasoning","I'm Not Trying to Be Right. I'm Trying to Show My Reasoning.",[148,243,244],{},"If someone reads something I've written and walks away thinking \"I wouldn't have solved it that way, but I understand why you did\" — that's the article working exactly as intended. I'm not trying to hand anyone the definitive answer to how software should be built, because I don't think that answer exists in a form portable enough to hand over. What I can share honestly is how I reason through a hard problem, built from what's actually worked for me and what's genuinely failed. When I write about a hospital system, a banking application, or a distributed system's architecture, I'm not setting a standard for the industry to follow. I'm showing my work — and if I learn something next month that changes my mind, that's not a contradiction of what I wrote before. It's just the job continuing to do what it's always done to me: reward curiosity over certainty, every time I've let it.",[246,247],"hr",{},[155,249,251],{"id":250},"references-and-further-reading","References and Further Reading",[253,254,255,264],"ul",{},[256,257,258,259,263],"li",{},"Michael Nygard, ",[177,260,262],{"href":179,"rel":261},[181],"\"Documenting Architecture Decisions\""," (2011) — the original proposal for Architecture Decision Records, capturing context and trade-offs rather than a universal \"right\" answer.",[256,265,266,267,271],{},"Laws of Software, ",[177,268,270],{"href":234,"rel":269},[181],"\"Cunningham's Law\""," — the (loosely attributed, half-apocryphal) adage that correction spreads faster than agreement, and why that shapes how good technical discussion actually happens.",{"title":273,"searchDepth":274,"depth":275,"links":276},"",1,2,[277,278,279,280,281,282,283,284,285,286],{"id":157,"depth":275,"text":158},{"id":164,"depth":275,"text":165},{"id":171,"depth":275,"text":172},{"id":195,"depth":275,"text":196},{"id":202,"depth":275,"text":203},{"id":213,"depth":275,"text":214},{"id":220,"depth":275,"text":221},{"id":227,"depth":275,"text":228},{"id":240,"depth":275,"text":241},{"id":250,"depth":275,"text":251},"2026-08-06","Everything here is a report from where I'm standing, not an argument built to relocate you there. If you land somewhere different after actually thinking it through, that's not a miss — that's the point.","md",true,{},"/blog/why-writing-engineering-blogs-is-hard","8 min",{"title":143,"description":288},"blog/13.why-writing-engineering-blogs-is-hard","Field Notes","IOOwICjaiZgTXz79nFQFgtEc4gnGCXdoUqtHRRvy1b4",[299,304],{"title":300,"path":301,"stem":302,"description":303,"children":-1},"Experience Is Earned, Not Downloaded","/blog/experience-is-earned-not-downloaded","blog/12.experience-is-earned-not-downloaded","Credentials, tutorials, and AI compress the time it takes to encounter an idea. None of them shorten the years it takes to develop judgment about which ideas actually hold up once reality gets involved.",{"title":305,"path":306,"stem":307,"description":308,"children":-1},"If All You Care About Is Payday, Stop Pretending You Care About Engineering","/blog/payday-vs-engineering","blog/14.payday-vs-engineering","A salary rewards your employment. It doesn't validate your judgment. Coasting through the month without questioning anything is a choice you're entitled to make — just don't call it engineering.",1785744834584]