Topic
Testing & Quality
Tests, refactoring, static analysis, and quality practices that protect intent over time.
Split AI Changes into Pull Requests Teammates Can Review
Keep AI-generated code accountable by narrowing scope, rebuilding coherent commits, and splitting risky changes into pull requests teammates can understand.
Change Engineering Culture Through Trust and Small Wins
Learn a new organization before prescribing fixes, earn trust through delivery, and let visible gains from tests and shared policy work spread beyond your team.
Oracle to MySQL Migration with Adapters, Dual Writes, and Flags
A reversible Oracle-to-MySQL migration using compatible adapters, dormant deployments, dual writes, tested switches, deliberate rollback, and legacy cleanup.
How to Break Long Legacy Migrations into Small, Deployable Steps
Break exhausting legacy work into small development, verification, and deployment increments that create feedback before a perfect rewrite.
Developer Testing Responsibility Before QA Handoff
Verify expected behavior before QA handoff so testers can pursue edge cases, reduce release loops, and build stronger cross-functional trust.
Refactor Spaghetti Code with Characterization Tests
Capture legacy behavior with broad API tests, refactor behind the safety net, and deploy small reversible changes while coverage grows.
Vibe Coding Still Requires Engineering Judgment
Use AI coding tools without surrendering problem definition, code review, quality standards, or the technical knowledge needed to verify results.
What Senior Engineers Owe the Developers Who Come Next
Seniority is more than tenure: learn how maintainable code, tests, guidance, and team continuity define responsible senior engineering.
Split God Query Methods by Purpose
Replace universal dynamic queries with purpose-specific methods, migrate callers incrementally, and shrink the side-effect surface before deeper refactoring.
Legacy Cleanup Starts with Safe Dead-Code Removal
Shrink legacy systems safely by combining runtime evidence, database checks, API consumer verification, and incremental dead-code deletion.
Modernize Legacy Systems with Small Deployments
Reduce modernization risk with small verified deployments, practical rollback boundaries, visible progress, and value that survives a paused project.
Architecture Rules Need Team Judgment Before Automation
Balance automated layer enforcement against review capacity, repository scale, developer growth, and a team's need to reason about architecture.
Swagger vs REST Docs: Choose API Documentation by Context
Compare Swagger and REST Docs by test enforcement, code intrusion, customization, and a pragmatic migration path for legacy APIs.
The Common Module Trap: Organize Code Around Concrete Capabilities
Delay common modules until ownership is clear, and organize logging, exceptions, and shared protocols around concrete capabilities instead.
How to Break Up a Giant Service Class Without Guesswork
Split an oversized service by mapping concepts, tracing responsibilities, testing boundaries, and comparing concrete refactoring options.
Create a Deliberate Messy Boundary to Keep the Core Clean
Contain unavoidable complexity in an explicit outer boundary so limited engineering time can protect the system's core concepts.
Layering That Teammates Can Understand—and the Build Can Enforce
Define stable layer roles, constrain optional upper layers, and automate checks only when a team needs stronger enforcement.
Name Readers, Finders, and Searchers by Behavior
Distinguish Reader, Finder, and Searcher classes by direct reads, added filtering, and composite searches rather than result count.
Earn Your Interfaces: Abstraction After Evidence
Start with concrete code, extract interfaces from proven variation, and keep one-to-one abstractions only when they create a real boundary.
Authorization Without Redundant Reads
Resolve the logged-in user once, keep access checks focused, and separate system-wide administration from channel-level permissions.
Don't Distort Production Code Just to Make Tests Easier
Change production code when its behavior becomes clearer or legacy code needs a testing seam—not merely to expose values or methods that only tests use.
Your First Test Can Be Messy—Refactor It Later
Start with the test that proves the behavior you care about, mock enough to make it run, and improve test design through repeated use and review.
Design Performance Tests from Real Traffic Shapes
Estimate volume and arrival patterns before choosing a load test, add deliberate headroom, and require evidence only where traffic risk justifies it.
Reliable Database Tests Without a Shared Test Database
Choose mocks, an in-memory database, Testcontainers, or a real database by the failure you need to catch, while keeping ordinary tests isolated from shared infrastructure.
Software Should Survive the Developer Who Built It
Good company software reduces debt, fits the team's operating ability, and remains understandable and repairable after its original developer leaves.
Unit Tests Should Preserve Business Intent
A business-layer unit test is useful when it guides design, records meaningful behavior, or makes the next developer reconsider a risky change.
Use Reader and Writer Components to Reveal Business Flow
Reader and writer components can hide storage details, narrow change, and let the business layer show policy, but only when the software's lifetime justifies them.
How to Split a Large Service Class by Responsibility and Layer
Use constructor dependencies and imports to diagnose an oversized service, then separate cohesive responsibilities before adding another architecture label.