Topic
Backend Engineering
Application services, frameworks, and implementation patterns for maintainable backend systems.
Split AI Development Work by Context, Device, and Risk
Combine IDE review, desktop agents, and a remote mobile workflow by assigning each task according to context size, inspection needs, and operational risk.
Evolve Development Templates from Observed AI Workflows
Update development templates from repeated AI-assisted use, while keeping validation explicit and project-specific instructions out of universal defaults.
Store Object Keys, Not File URLs
Design file uploads around stable object keys, server-composed delivery URLs, collision-resistant names, and separately retained original filenames.
AI Output Is Not the Same as Engineering Productivity
AI agents can raise output, but complex work still depends on context, memory, judgment, and the self-directed thinking of strong teammates.
Judge AI Practices by Real Workplace Utility, Not Buzzwords
Test AI practices in real work instead of chasing labels, and use firsthand experience to separate durable methods from marketing-driven fear.
Choosing Project Structure and Tech Stacks in the AI Era
Choose project boundaries and technology stacks by team size, existing expertise, reviewability, and failure cost—not AI output speed alone.
Simplify Modules and Layers for AI-Assisted Greenfield Work
Explore fewer modules, interfaces, and layers for AI-assisted greenfield work, while protecting clarity, quality, and incremental treatment of legacy systems.
Coordinate Account Deletion Cleanup Without a Message Broker
For small services, collect domain-specific cleanup handlers behind one interface so account deletion stays explicit without extra messaging infrastructure.
Split All-in-One Libraries into Composable Dependency Modules
Avoid hidden infrastructure, runtime cost, and dependency conflicts by splitting company libraries into modules each service can understand and compose deliberately.
Server-Driven API Responses for Mobile App Compatibility
Keep changing display policy and experiments server-side for old app versions, but isolate that translation at the API boundary to protect the domain model.
Avoid the Admin Common Module That Owns Every Domain
Keep admin features from reversing domain dependencies. Preserve service boundaries and accept small duplication while admin requirements continue to diverge.
Java Optional Rules: Keep Absence at Data Boundaries
Use Java Optional where a missing return value gives callers a real choice, while keeping fields, parameters, and trusted internal flows explicit.
Design Distributed Tracing and Safe Production Logs Together
Correlate requests across services while controlling log volume, masking sensitive fields, limiting access, and retaining only useful operational data.
Why Pagination Count Queries Slow Down Databases
Understand why exact totals can cost more than limited queries and when to consider slices, cached metadata, estimates, or product changes.
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.
Reduce Account-to-Profile Linkability in Anonymous Services
Explore identifier separation, disposable mapping keys, minimal retention, and the recovery tradeoffs behind a privacy-oriented account model.
Keep Guest Data Masking in the Presentation Layer
Learn why guest masking and author flags belong at the presentation boundary, keeping domain logic clean while preventing raw data exposure.
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.
Paginated Like Counts: Start with a Simple Query
Count likes for the current page with a bounded query before adding counter columns or Redis and their added management costs.
Translate Exceptions at Module Boundaries
Keep implementation exceptions inside their modules and define explicit translation boundaries that prevent dependencies from leaking upward.
Before Fixing Circular Dependencies, Fix the Domain Model
Resolve confusing ownership and concept boundaries before using interfaces or dependency inversion to treat an apparent circular reference.
Nullable or Zero? Choosing a Kotlin JPA Entity ID Strategy
Compare nullable and zero-valued numeric IDs for Kotlin JPA entities, including new-entity detection, ambiguity, and verification.
One Project, Many Deployables: Drawing Boundaries by Runtime Role
Separate public, admin, batch, and operations workloads into runnable applications without splitting the whole codebase into separate projects too early.
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.
Compose My Page Data Without Inventing a My Domain
Build My page summaries through a dedicated composition layer while keeping user, order, and product responsibilities in their proper domains.
Why a My Page Is Not a Domain
A My Page is a client-facing view, not a business domain. Keep orders, products, and coupons with their real owners and combine them at the boundary.
Designing Domain Models Beyond the Shape of the UI
A tree-shaped API does not require a tree-shaped domain. Keep the client contract at the presentation boundary and compose it from internal concepts.
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.
Keep API Request Models Out of the Core Domain
Convert external request objects into business-owned values at the presentation boundary so the API depends inward and the core never depends back.
Foreign Keys Are an Operational Tradeoff, Not a Rule
Choose foreign keys, indexes, and ORM mappings from integrity needs, incident response, deployment practice, and who operates the database.
Give Cross-Domain Behavior to the Domain That Owns It
Before placing a caller-owned interface across modules, identify which domain owns the action and let callers depend on that capability.
Who Owns the Enum? Dependency Design Across Domain Modules
Put a business enum with its domain, let storage depend inward, and use a small shared enum module only while the domain boundary is still emerging.
Provider Identity and Authentication Boundaries for Platform APIs
How a small platform API resolves provider keys into a domain identity, separates tenant data, and chooses an authentication boundary that fits its scale.
Domain Models First: A Conservative Approach to JPA Associations
A cautious way to map JPA entities: start with IDs, add associations only when lifecycles truly align, and model business concepts independently.
You Can Practice High-Traffic Engineering Without Real Traffic
Build and operate a small service, create load deliberately, and show how you found and fixed bottlenecks without pretending a load test equals production experience.
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.
Do Not Create a DTO for Every Layer by Habit
Use DTOs where data crosses a boundary with a different contract. Mapping has a cost, but so does letting external request shapes define the inside of a service.
Put Circuit Breakers Next to the Failing I/O
Circuit breakers, timeouts, cache fallbacks, and remote-call policies belong near the implementation that performs the I/O, while domain code chooses the required behavior.
Timeouts Are Product Decisions, Not Just Client Settings
Design timeout, retry, and recovery policies from the user's waiting budget and the uncertainty of each failure instead of applying one retry rule everywhere.
Do Not Split Modules Before the Domain Has Matured
Domain maturity comes from understanding policy, behavior, and operating reality. Let those lessons reveal module boundaries instead of freezing guesses too early.
Upgrade Dependencies Before the Gap Becomes a Project
Frequent, prioritized dependency upgrades keep change small, expose compatibility problems early, and prevent a maintained service from quietly accumulating debt.
When an AI Agent Calls the Same Tool Twice
Retries are normal in distributed systems and AI workflows. Use database constraints, idempotency keys, and bounded retries to prevent duplicate side effects.
Gradle Dependency Scopes Are Architectural Boundaries
Use Gradle implementation, api, runtimeOnly, and compileOnly deliberately to express module access, prevent accidental coupling, and preserve design intent.
Isolate Admin Work from the Service Domain
Admin APIs have different queries, mutation needs, and release risks. Isolate them by module or repository instead of letting operational convenience reshape the core service.
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.
Carry One Trace ID across Distributed Services
Logs become operationally useful when one request can be followed across HTTP calls and asynchronous events without exposing sensitive data or flooding storage.
Reuse Below the Use-Case Layer
When use cases call one another for reuse, business changes spread and cycles follow. Compose above them or extract focused implementation components below 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.
Choose Normalization from Requirements and Object Relationships
Database normalization is not a score to maximize. Decide from change semantics, query cost, and the relationships the data is meant to preserve.