In Holonic Asset, I was responsible for the overall engineering quality gates.
The determination to establish these gates arose when a teammate submitted our very first Pull Request.
Looking at the repository, branches and commit messages were named with patterns like initials_what_i_did. Before even reviewing the code logic, branches and commit logs were already in disarray. This brought a stark realization: if conventions remain verbal agreements or Slack reminders, as soon as deadlines loom, everyone falls back on divergent personal habits.
Yet simply writing a formal “Please follow these rules” document is useless—no matter how grand the rollout, it becomes shelfware within days. Conversely, blindly porting a rigid, heavyweight ruleset from a legacy enterprise project only incentivizes developers to bypass the gates. The only sustainable way to protect engineering quality is to make best practices the unavoidable default path in daily development—requirements defined first, changes bounded, commits automatedly verified, and merges strictly reviewed. As long as developers follow the default path, they naturally do things right.
1. Not a Checklist, But a Guided Highway
During rapid iterations with multi-developer collaboration, software projects easily lose control across four critical stages:
| Failure Mode | Typical Symptom | Downstream Cost |
|---|---|---|
| Quality Decay | Formatting, Linting, tests, and builds scattered across local machines | Issues discovered late; PR reviews devolve into endless low-level nitpicking |
| Contract Drift | Frontend and backend sync API field changes verbally | Missing fields, runtime type errors, blocked integration and deployments |
| Runtime Desync | Business DB writes and async task queue dispatches are decoupled | Lost tasks, dangling jobs, difficult post-mortem recovery |
| Scope Creep | Unclear Issue scopes, PRs lacking reproducible validation evidence | PRs balloon indefinitely; unvetted risks slip directly into main |
Relying on manual code review to catch every issue is unsustainable. Reviewers get tired, overlook nuances, and should never spend mental energy checking whitespace or verifying JSON field names.
To prevent these breakdowns, we built an integrated, default engineering path rather than a passive checklist:
We distilled the entire change lifecycle into four core tenets:
- Define Requirements First: Clarify problem context, scope, exclusions, and acceptance criteria upfront.
- Bound Changes: Keep module responsibilities clean; prevent PRs and commits from ballooning.
- Verify Every Commit: Perform fast pre-commit checks locally; leave full test suites and production builds to remote CI.
- Tiered Merge Reviews: Machines handle repetitive validation; humans evaluate design, backwards compatibility, and architectural risks.
Individually, these tenets are standard. What makes them effective is connecting them into a closed loop: structured Issues give design decisions a firm anchor; bounded changes keep reviews focused; automated CI ensures code quality is never backed by mere “looks good to me.”
2. Rome Was Not Built in a Day: A Phased Rollout
We did not impose all rules at once. The engineering gates evolved across four deliberate phases:
Phase 1: Unifying Collaboration Entry Points
In mid-July 2026, we established main branch protection rules, structured Issue forms, PR templates, and repository-specific developer skills:
- PR #29: Establish Bug, Feature, Document, and Experiment Issue templates
- PR #37: Introduce Issue, Branch, Commit, PR, and Decomposition Skills
- PR #42: Add a unified PR description template
This phase solved alignment: “Are we talking about the same problem?” With unified entry points, context, scope, and verification proofs were readily accessible, preventing mid-implementation debates over feature boundaries.
Phase 2: Delegating Repetitive Checks to Tooling
From late July to early August 2026, we configured unified pre-commit hooks, automated frontend/backend CI workflows, and comprehensive setup documentation:
- PR #56: Standardize Lefthook pre-commit configuration for frontend and backend
- PR #57: Establish GitHub Actions CI for frontend and backend
- PR #106: Add bilingual Lefthook installation and troubleshooting guides
This liberated human review from formatting and syntax errors. The governing principle: run fast checks locally in seconds, and offload time-consuming full test suites and production builds to remote CI.
Phase 3: Contract Pipelines, Code Coverage, and Module Responsibilities
From August 2026 onwards, we implemented backend-driven contract generation, tracked test coverage via Codecov, and formalized module boundaries:
- PR #60: Generate OpenAPI specs and frontend types from Go DTOs as the source of truth
- PR #155: Add frontend test coverage reporting and Codecov integration
- PR #157: Add backend test coverage reporting and Codecov integration
Here, governance penetrated the architectural layer: correct formatting does not prevent API drift, nor does a passing build guarantee that critical paths are covered by tests.
Phase 4: Continuous Evolution Driven by Real-World Issues
Engineering rules are living code, not static documentation. Whenever we hit real issues—such as API drift, unnecessary builds, CORS mismatches, or reverse module dependencies—we formalized the solution into a new lint rule or automated check.
3. How Frontend CI Gradually Tightened Boundaries
Frontend static analysis underwent three distinct iterations:
Stage 1: Protecting the types Export Entry Point
PR #64 first used Oxlint to enforce module boundaries by banning direct deep imports into types:
@/model/<module>/types/**@/features/<feature>/types/**It mandated that Model and Feature types be exported through the module’s index.ts, curbing type dispersion and circular imports. However, its limitation was only catching paths containing types—components, hooks, and internal API functions could still be penetrated.
Stage 2: Expanding from Import Restrictions to General Code Quality
PR #275 introduced Oxlint plugins for React, React Perf, TypeScript, Unicorn, Promise, Import, and Vitest, comprehensively vetting Hook dependencies, self-references, async handling, and test assertions.
Alongside this upgrade, the codebase unified import type usage and refactored .then()/.catch() chains into clean async/await syntax. The result: 388 frontend files achieved 0 errors and 0 warnings, while all 377 tests across 81 test suites passed cleanly.
Stage 3: Encoding Unidirectional Dependencies into Lint Rules
PR #301 eliminated the vulnerability of treating folder structures as mere “gentlemen’s agreements.” Previously, despite dividing directories into app, pages, features, and model, external modules could still bypass public entry points and import deep internals like @/features/generation/generation-queue.
PR #301 locked down strict top-down unidirectional dependencies:
The rules enforce two guarantees:
- Module Encapsulation: Cross-module imports must target the public root
@/features/<feature>or@/model/<module>without deep penetration (with@/model/generated/**as a legitimate exception). Internal intra-module references continue using relative paths. - Banning Reverse Dependencies:
modelmay never import fromapp,pages,features,components, orhooks;featuresmay never import fromapporpages;- Shared layers like
components,hooks, andlibmay never import from upper-level business modules; pagesis strictly forapprouting assembly and cannot referenceapp.
As soon as these rules landed, latent boundary violations were surfaced: GenerationQueue and CreateAssetForm were routed back through unified module exports, internal documentation feature references were normalized to relative paths, and login redirect checks were relocated from app back to the Auth Feature. Architectural boundaries no longer rely on human vigilance—they are enforced by pnpm lint and CI.
4. The Hardest Part Is Not Writing Rules, But Making Them Ergonomic
| Rollout Friction | Real-World Symptom | Targeted Remedy |
|---|---|---|
| Tooling Confusion | New contributors unsure how to configure pre-commit hooks | Authored bilingual installation and troubleshooting guides |
| Heavy Local Latency | Full builds on every commit caused disruptive lag | Kept local checks lightweight; relegated full test suites to remote CI |
| CI Waste | Markdown/doc edits triggered expensive preview builds | Added path filters to skip redundant build pipelines |
| Contract Desync | Backend schema changes broke frontend builds unexpectedly | Automated frontend type generation directly from backend Go DTOs |
| Architectural Drift | Deep internal imports and reverse dependencies | Encoded unidirectional dependency rules directly into Oxlint |
| AI Agent Inconsistency | Agent outputs drifted from repository conventions | Crafted dedicated AGY Skills requiring agents to read templates before generating code |
Whenever we introduce a new quality gate, we must answer two questions: What real risk does this prevent? How much friction does this add to daily development? If the friction outweighs the benefit, the rule is premature.
Related tracking Issues:
- Pre-commit hook setup guide
- API field mismatch breaking builds
- Skipping redundant preview builds
- Frontend dependency direction check
5. Why the Team Actually Complies
Expecting everyone to voluntarily maintain perfect discipline is unrealistic. We structured compliance into four progressive layers:
- Clear & Understandable: Structured Issue/PR templates and documentation provide canonical examples.
- Easy to Execute: Auto-generated types and dedicated repository Skills make the right approach the path of least resistance.
- Automated Feedback: Pre-commit hooks, remote CI, and preview deployments provide immediate feedback.
- Hard Gate Enforcement: Main branches are protected, requiring PRs, at least 1 approval, all review conversations resolved, and squash-only merges.
Code review itself is explicitly tiered:
- Machine Tier: Automates formatting, linting, tests, builds, race detection, and preview environments.
- Engineering Pre-review: I evaluate module boundaries, contract consistency, test proofs, and backwards compatibility.
- Architectural Review: Senior mentors handle final sign-offs on high-risk architectural proposals and key schemas.
This tiered system exists because machines and humans excel at different things: using human eyes to check syntax and formatting wastes energy, while expecting machines to evaluate cross-module architectural integrity is unrealistic.
6. Feedback Speed Is as Important as Rule Coverage
The golden rule of quality gate design is tiering: shift lightweight, fast checks left into the local environment, and consolidate heavy, thorough verification on the remote pipeline.
Local Lefthook hooks intercept syntax, formatting, and basic static errors in seconds. Remote CI then runs race detectors, type checking, and full test suites. This catches elementary mistakes locally in seconds without turning every commit into a bottleneck.
7. What Vercel Preview Deployments Actually Catch
Passing CI only proves that “code compiles and unit tests pass”—it cannot prove that “the system works under real network conditions.” Automated Vercel PR preview environments caught two classes of production bugs completely invisible to local testing:
Object Storage CORS Configuration
When the frontend requested images from object storage, presigned URLs were valid and the bucket returned content, yet browsers blocked the request with CORS errors. The root cause: local development origins (localhost:3000) differed from unique Vercel preview subdomains. Without a live preview, this would only have surfaced after deployment to production.
HTTPS Pages Failing to Reach HTTP Backends (Mixed Content)
During local development, both frontend and backend ran over HTTP. However, Vercel preview environments enforce HTTPS by default, while our staging backend initially exposed only HTTP ports. Modern browsers block HTTPS pages from calling insecure HTTP endpoints (Mixed Content restrictions). The code logic was flawless, yet the application was paralyzed. This drove us to configure backend domain certificates and enforce full-pipeline HTTPS early.
| Real-World Production Issue | Why Local Tests Missed It | Core Problem Exposed by Preview Env |
|---|---|---|
| Object Storage CORS Block | Local origins were whitelisted or same-origin | Ephemeral preview domains were missing from bucket CORS whitelist |
| Blocked HTTP Backend Calls | Both frontend and backend ran over HTTP locally | Browser blocked HTTPS web app from calling insecure HTTP API |
8. Hard Lessons from Contract Drift
Contract desynchronization between frontend and backend was once the most exhausting friction during integration: the backend renamed a field, and the frontend only discovered it when a page crashed or a build failed.
We established a unidirectional source-of-truth pipeline:
- Request and response structures are exclusively defined in backend Go DTOs;
- Tooling automatically generates OpenAPI specifications and documentation from DTOs;
- The frontend generates TypeScript types directly from OpenAPI specs, integrating them into compilation and unit testing.
Field renames, enum modifications, or nullability changes are caught immediately at compile time with exact code line references, eliminating reliance on verbal syncs.
9. Engineering Governance Beyond CI
Holonic Asset is an AI game asset production platform with a long, heterogeneous pipeline: upstream integrates stochastic multimodal foundation models, midstream runs deterministic image/video processing, and downstream manages persistence and async jobs.
If modules call each other arbitrarily, a green CI only proves “it runs right now,” not that root causes can be diagnosed when failures occur. Beyond code entry checks, we partitioned clear domain boundaries across the system:
| Module | Core Responsibility | Explicitly Not Responsible For |
|---|---|---|
| Domain | Project, Asset, and Record core domain entities | AI API calls and task scheduling |
| Generator | Model orchestration and generation strategies | Low-level media processing algorithms |
| Processor | Deterministic image processing (keying, slicing, scaling) | Provider selection and model interactions |
| Task | State machines, queue consumption, and transactional outbox | Concrete asset business processing logic |
| Storage | Presigned uploads, object storage persistence, URL translation | Asset domain lifecycle |
Benefit 1: Rapid Root-Cause Analysis and Bounded Blast Radius
With clear responsibilities, troubleshooting production incidents becomes deterministic:
- Generation Quality or Prompt Anomalies: Isolated strictly to the Generator module, ruling out media processing algorithms. Swapping model providers (e.g., Midjourney / Flux / SD) never impacts downstream frame slicing or DB logic.
- Keying Halos, Aspect Ratio Distortion, Format Corruption: Guaranteed to stem from deterministic algorithms in the Processor module. These pure algorithms run unit tests locally without external network dependencies or expensive model mocks.
- Expired Presigned URLs or CDN Glitches: Triage is confined entirely to the Storage module, decoupled from asset business logic.
Benefit 2: Eliminating “Zombie Tasks” via Transactional Outbox
The most insidious asynchronous bug occurs when “business state is written to the database, but network jitter fails the message dispatch to the task queue.” Without transactional protection, the UI indicates task creation success while the job hangs indefinitely in PENDING. Operators cannot tell whether the message was lost or the worker crashed, forcing manual database interventions.
To resolve this, the Task module implements the Transactional Outbox pattern:
- Local Transactional Consistency: Writing the business asset state and inserting the pending outbox record happen within a single ACID database transaction. If the transaction fails, everything rolls back, completely eliminating zombie tasks.
- Traceable Retries: An independent Dispatcher polls pending outbox events and pushes them to the River queue. If dispatch fails, the retry count increments while preserving event context. Debugging stuck tasks requires only a glance at
outbox_eventsdelivery status, avoiding log dives. - Business Decoupling: Business code focuses on defining task payloads and execution handlers, remaining agnostic to underlying queue implementations.
10. The Role of AI in Engineering Quality
In Holonic Asset, an inescapable reality is that nearly every line of code was written by AI.
When the primary coding driver shifts to AI, the common concern is: will AI rapidly produce volumes of technical debt and architectural bad smells? In practice, as our engineering closed loop matured, we found that AI is not a destroyer of quality; on the contrary, across many dimensions of quality control, it outperforms human engineers:
- Context-Rich PR & Issue Synthesizer: AI excels at generating structured Issue breakdowns and PR descriptions. Because it traverses the entire diff and call graph, it often lists subtle edge cases, impacted functions, and test assertions more accurately than the developer who wrote the initial prompt.
- Tireless, Hyper-Vigilant Micro-Reviewer: In localized code reviews, AI exhibits remarkable insight. Confronted with concurrency race conditions, edge-case null pointers, memory leaks, or unhandled async exceptions, AI catches bugs with greater consistency than fatigued human reviewers. Most importantly, AI never suffers review fatigue, maintaining uncompromising standards across dozens of reviews a day.
However, what can AI NOT do?
While AI excels at micro-level implementation and localized audits, it cannot replace humans in several critical domains:
- AI Cannot Supply Architectural Taste: AI conforms to the smells in its context. If a codebase is riddled with reverse dependencies and blurry boundaries, AI will happily build upon the mess, producing code that “works locally but degrades the system globally.” Defining architectural boundaries (such as unidirectional dependencies and the Transactional Outbox pattern) must be driven by humans.
- AI Cannot Make Engineering and Business Trade-offs: Deciding when to relax rules for velocity, when to enforce strict gates for stability, and when to adopt new paradigms are human trade-offs.
- AI Cannot Take Accountability: AI feels no responsibility and cannot be held accountable for production outages. Guaranteeing sound architecture and protecting the integrity of the main branch remains an exclusively human responsibility.
| Development Stage | AI Superpowers (Machine Strengths) | Engineering Defenses (Human Roles) |
|---|---|---|
| Requirements & Scoping | Rapidly expands high-level requirements into structured Issues and checklists | Clarifies business goals; bounds scope and explicit exclusions |
| Code & Architecture | High-speed generation of boilerplate, unit tests, and type implementations | Designs unidirectional dependencies; establishes domain boundaries |
| PRs & Traceability | Deeply parses diffs to generate comprehensive change logs and impact analyses | Verifies business logic matches intent; endorses merges and releases |
| Review & Merging | Millisecond-level scans for concurrency races and boundary anomalies | Evaluates systemic cohesion, backwards compatibility, and blast radius |
Consequently, we do not treat AI as a threat to be guarded against, nor do we naively grant it unconstrained autonomy. The correct approach is to encode engineering standards into Lint rules, data contracts, and specialized Agent Skills: enabling AI to maximize its productivity and review acuity within protected guardrails, while human engineers focus on top-level architecture, policy design, and final verification.
11. Summary: Making the Right Way the Default Path
Engineering quality gates do not exist to add bureaucracy, nor are they intended as a test of who can memorize guidelines.
A team’s engineering quality is not defined by the ceiling reached when members are at their sharpest, but by the floor sustained when everyone is rushing to meet deadlines. By baking standards into tooling, encoding boundaries into compilers, and automating verification, following the default path naturally leads to doing things right.