Mert Özen Further With Every Line

Blog

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

Series
106 posts found
Automating the Rules: Pre-Commit Hooks, CI Gates, Gradual Adoption in Legacy Codebases and .git-blame-ignore-revs
Software Design PART 05

Automating the Rules: Pre-Commit Hooks, CI Gates, Gradual Adoption in Legacy Codebases and .git-blame-ignore-revs

A pre-commit hook gives fast feedback but is skipped by --no-verify; the only place that stops a convention violation before main is a required CI check. The post shows formatting only staged files with Husky.Net and lint-staged, CI steps that name the fix command, choosing between touched files and a big bang in legacy code, .git-blame-ignore-revs, updating open PRs without conflicts and a suppression policy, all with real output.

14 Nov 2026 15 min 7
Conventions in Angular and TypeScript: The Style Guide, Prettier and Dividing Responsibility with ESLint
Software Design PART 04

Conventions in Angular and TypeScript: The Style Guide, Prettier and Dividing Responsibility with ESLint

In an Angular project formatting belongs to Prettier, naming and selector rules to ESLint, and eslint-config-prettier draws the line between them: without it, two copied @stylistic rules produced 110 errors on Prettier-formatted code. The post covers the Angular 22 Style Guide's decisions, keeping suffixed names in older projects, Prettier flagging 7 files in a fresh project, the naming-convention rule and ESLint 10 ignoring .eslintrc.

14 Nov 2026 12 min 7
Choosing a C# Formatter: dotnet format or CSharpier? Giving Each Rule a Single Owner
Software Design PART 03

Choosing a C# Formatter: dotnet format or CSharpier? Giving Each Rule a Single Owner

When two formatters own the same rule the loop never ends: on a 1,000-file Bookstore solution, dotnet format found 6,002 problems in CSharpier's output and CSharpier found 1,001 unformatted files in dotnet format's. The fix is to give whitespace to CSharpier and style to the analyzer; the post compares both tools' output and timings, shows why turning off IDE0055 is not enough, and pins the version with a dotnet tool manifest.

14 Nov 2026 12 min 6
Encoding C# Conventions in .editorconfig: Code-Style Options, Naming Rules and Enforcing on Build
Software Design PART 02

Encoding C# Conventions in .editorconfig: Code-Style Options, Naming Rules and Enforcing on Build

A naming rule's own severity line is not read on build; to enforce the rules in dotnet build you need dotnet_diagnostic.IDE1006.severity alongside EnforceCodeStyleInBuild. The post covers the .editorconfig hierarchy, the IDE rule IDs behind code-style options, the symbols-style-severity triple of naming rules, a test folder override, how category-Style enforces defaults you never chose, and rules that run only in the IDE, all with real build output.

14 Nov 2026 12 min 5
What Are Coding Conventions and Why Leave Them to Tools, Not People? Sources, Levels and Team Decisions
Software Design PART 01

What Are Coding Conventions and Why Leave Them to Tools, Not People? Sources, Levels and Team Decisions

The value of a coding convention comes not from the rule itself but from it being settled and enforced by a tool: in Bookstore a one-line discount change was buried in a 55-line diff by editor settings, and .editorconfig plus dotnet format brought it back to one line. The post covers the formatting, style, naming and architecture levels, choosing a source and making the team decision once and recording it.

14 Nov 2026 12 min 4
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
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
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
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
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
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
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 3
Blog Map