Mert Özen Further With Every Line

Blog

Mert Özen's blog posts on software development, backend, frontend, and DevOps.

Series
9 posts found
What Refactoring Is and Isn't: Preserving Behavior, the Two Hats and the Rewrite Decision
Software Design PART 01

What Refactoring Is and Isn't: Preserving Behavior, the Two Hats and the Rewrite Decision

Refactoring means changing internal structure in small, safe steps without changing observable behavior; without a test that measures behavior, what you do is not refactoring but a change made in hope. The post shows a PR titled "no behavior change" shifting an invoice by one cent, the two hats and separate-commit discipline, when to refactor and the criteria for a rewrite decision, all with real output.

14 Nov 2026 12 min 2
A Safety Net Before Refactoring: Characterization Tests, Approval Testing and Finding Blind Spots
Software Design PART 02

A Safety Net Before Refactoring: Characterization Tests, Approval Testing and Finding Blind Spots

Before refactoring untested legacy code you need to record today's behavior with characterization tests and approval testing; but you can't trust the net until coverage and deliberate mutations show it really protects you. The post approves an untested InvoiceRenderer with Verify, fixes date and culture breakages with scrubbers, and shows with real output how two mutations survived despite a 48-combination snapshot and 100 percent coverage.

14 Nov 2026 12 min 2
Small, Safe Steps: IDE Refactorings, Parallel Change and Bulk Automated Fixes
Software Design PART 03

Small, Safe Steps: IDE Refactorings, Parallel Change and Bulk Automated Fixes

The safe way to open up a large method is small steps, each compiled, tested and committed on its own; a signature change is carried out with parallel change as expand, migrate, contract. The post opens PricingEngine.Calculate in 14 commits with no red commit, puts a dotnet format bulk fix in its own commit, and shows with real output a snapshot catching a decimal scale difference and renames silently breaking a JSON contract and convention-based DI registration.

14 Nov 2026 13 min 2
From a Pile of Conditionals to Readable Design: Switches, Flag Parameters and Rule Tables
Software Design PART 04

From a Pile of Conditionals to Readable Design: Switches, Flag Parameters and Rule Tables

There is no single right target for nested conditionals: polymorphism is cheaper when new types arrive often, a switch expression when new operations do, and a rule table when rules change often and share one shape. The post turns PricingEngine's discount conditionals into all three targets through the same 144-combination snapshot, with an enum, guard clauses, a split flag parameter and a Percentage type, and shows real CS8509, CS0535 and missing-rule test output.

14 Nov 2026 13 min 2
Breaking Dependencies in Legacy Code: Sprout, Wrap, Extract Interface and Static Dependencies
Software Design PART 05

Breaking Dependencies in Legacy Code: Sprout, Wrap, Extract Interface and Static Dependencies

The safe way to add behavior to an untested legacy class is to find the change point and the test point first and open seams only for the obstacles between them: the new rule is tested separately with sprout, the constructor is parameterized, the static call moves behind a thin wrapper and time moves to TimeProvider. The post applies these steps to an OrderImportJob stuck on SQL Server; the tests catch an unknown assumption of the job and a bug in the line wiring the sprout, with real output.

14 Nov 2026 11 min 2
Planning a Large Refactoring: The Mikado Method, Branch by Abstraction and Parallel Run
Software Design PART 06

Planning a Large Refactoring: The Mikado Method, Branch by Abstraction and Parallel Run

A refactoring that doesn't fit in one PR is planned with a prerequisite graph found by trying and reverting with the Mikado Method, and carried out on the main branch with branch by abstraction in small commits that each stay green. The post replaces the static LegacyShippingClient with a typed HttpClient-based client in 13 commits; a parallel run catches two differences no test covered, and real output shows how static state breaks parallel tests.

14 Nov 2026 13 min 2
Refactoring When Data and Contracts Change: Renaming an API Field, an EF Core Property and an Angular Model Together
Software Design PART 07

Refactoring When Data and Contracts Change: Renaming an API Field, an EF Core Property and an Angular Model Together

When a property is renamed, the external contracts (JSON field, database column, CSV header, cached shape) must be separated from the code and moved step by step with expand-contract. The post renames Book.Price to ListPrice and moves DiscountRate to its own table across .NET, EF Core, SQL Server and Angular, showing with real output a broken view, a cache read as zero, a tolerant reader and a script that bypassed the dual write.

14 Nov 2026 11 min 2
Refactoring Angular Code: From NgModules to Standalone, Constructor Injection to inject(), RxJS to Signals
Software Design PART 08

Refactoring Angular Code: From NgModules to Standalone, Constructor Injection to inject(), RxJS to Signals

The safe way to modernize an old Angular component is to pin its behavior with tests first, run the official migration schematics feature by feature with --path, each in its own commit, and convert by hand, under the tests, the data flow the schematics can't touch. The post migrates a CheckoutComponent with eight schematics plus a manual switchMap and signals step, showing with real output prune-ng-modules breaking the build, a stream ending after an error and a doubled request.

14 Nov 2026 13 min 2
Prioritizing Technical Debt and Making the Case for Refactoring: Hotspot Analysis, Measurement and Talking to the Product Owner
Software Design PART 09

Prioritizing Technical Debt and Making the Case for Refactoring: Hotspot Analysis, Measurement and Talking to the Product Owner

Refactoring investment goes not to the ugliest code but to the complex code that changes most often, the hotspots, and is explained to the Product Owner in terms of cost of change, lead time and failure rate. The post runs a small C# script that uses git log and Roslyn to compute Cognitive Complexity on the Bookstore repo, showing with real output PricingEngine as the top hotspot, 59 percent change coupling with InvoiceRenderer and eight invoice bugs.

14 Nov 2026 11 min 2
Blog Map