1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  7. 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.

  8. 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.

  9. 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.

  10. 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.

  11. 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.

  12. 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.

  13. 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.

  14. 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.

  15. 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.

  16. 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.

  17. 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.

  18. 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.

  19. 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.

  20. Authorization Without Redundant Reads

    Resolve the logged-in user once, keep access checks focused, and separate system-wide administration from channel-level permissions.

  21. 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.

  22. 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.

  23. 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.

  24. 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.

  25. 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.

  26. 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.

  27. 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.

  28. 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.