# Agentic Workflow Source: https://lib.findy.co.jp/ai/agentic-workflow A delegation model where AI agents plan, implement, and verify on their own while humans set goals and judge the result, with context management at the core. ## Overview An agentic workflow is a development model that delegates implementation to AI agents: the human states the goal and judges the result, while the agent plans, implements, and verifies on its own. It differs from [vibe coding](/ai/vibe-coding), where a human works with an agent interactively — the working style shifts from collaboration to delegation, and the human moves from writing code to directing the work. In vibe coding, a human and an AI face each other one to one: the human does the work and the AI supports it. Agentic Workflow reverses that relationship. The AI does the actual work, and the human becomes an orchestrator who sets the direction and makes the judgments. Instead of conversing with a single AI, the human delegates many tasks to multiple agents at the same time — a 1:N way of working. | | Vibe coding | Agentic Workflow | | ------------------------ | ------------------- | ---------------------------------------------- | | Human-to-AI relationship | 1:1 | 1:N | | Who does the work | Human (AI supports) | AI (agents implement) | | Human's role | Worker | Orchestrator — sets direction, makes judgments | This page covers what makes delegation work: the autonomy of the agent, the foundation the repository must provide, the techniques — managing context, dividing roles along context boundaries, and implementing in parallel — and the division of responsibility between AI and humans. ## The four elements of agent autonomy Delegation relies on the agent driving work forward without step-by-step instructions. Four capabilities make that possible. The human specifies *what* to achieve; the agent assembles *how* — the concrete plan and the code. The agent splits a large task into subtasks and executes them in order. The agent operates files, commands, and skills, and reaches external systems through MCP as the work requires. The agent runs tests, reads the failures, fixes the code, and re-runs — repeating autonomously until the change passes. ## Prerequisites ### Guardrails An agent follows the signals the repository gives it, so those signals must exist in a machine-readable form before implementation is delegated. Four kinds of guardrails matter: Project documentation and agent instruction files (README, rules) that state the architecture, coding conventions, naming rules, and test policy. Unified type definitions and design patterns the agent can read and imitate. Test code sufficient for the agent's self-verification loop — run, fail, fix, re-run — to be meaningful. Appropriate pull request granularity and review culture. A weak foundation is amplified, not compensated. If conventions are inconsistent or tests are thin, agents amplify those weaknesses. Put the foundation in place before delegating. ### git worktree A git worktree creates multiple working directories for a single repository. Switching branches in place (`git switch`) allows only one branch at a time, so moving to another task means stashing or committing half-done work first. A worktree separates branches by directory instead: ```text theme={null} # Main working directory (feature/auth branch) ~/project/MyApp/ # Another working directory created as a worktree (fix/login-bug branch) ~/project/MyApp/.claude/worktrees/fix-login-bug/ ``` Each directory holds its own branch. This is what lets parallel agents work without interfering with each other: each agent gets its own directory, so one agent's changes never touch another's, and a build or test run in one worktree never blocks the others. Operated by hand, a worktree takes many commands from creation to cleanup: ```bash theme={null} # Create: add a worktree with a new branch git worktree add .claude/worktrees/fix-login-bug -b fix/login-bug cd .claude/worktrees/fix-login-bug # Set up: copy the files git does not track, install dependencies cp ../../../.env . cp ../../../localhost.pem . npm install # Clean up: return to the main directory, remove the worktree and branch cd ~/project/MyApp git worktree remove .claude/worktrees/fix-login-bug git branch -d fix/login-bug ``` Repeating this routine for every task is a burden, the cleanup is easy to forget — finished worktrees keep piling up — and the number of steps itself becomes a barrier when introducing worktrees to a team. Encapsulating the routine in a [skill](/ai/skill) reduces it to a single command such as `/git-worktree feature/add-auth`. Behind it, the worktree is created, the session's working directory switches over, and the configuration files are copied automatically. The design point is separating what never changes from what does: the worktree-creation logic is identical in every project and lives in a shared skill, while the project-specific setup — which environment files to copy, how to install dependencies — is overridable by a script placed in each repository. Because the worktree operation stands alone as a skill, an implementation agent does not need to know how worktrees are made. It calls the skill as one part of its flow — fetch the issue, create the worktree, implement, open a pull request, clean up — and when the procedure changes, only the skill needs updating. ## Techniques to make Agentic Workflow work ### Manage context An AI's accuracy degrades as its context — the information it holds while working — grows. The longer an agent runs on its own, the more this matters, so whether delegation works comes down to how the context is designed. Every technique in this section follows from that single point. Within a single agent, three practices keep the context lean: * **Offload heavy exploration to subagents.** Information-heavy work such as codebase exploration and research is handled by separate agents, which return only a summarized result to the main agent. * **Move procedures and guidelines into skills.** Rather than loading them into the context permanently, the agent references them as a [skill](/ai/skill) when needed. * **Keep only what decisions need in the main context.** By not carrying intermediate output and raw data, the agent finishes long tasks without losing accuracy. What the three have in common: bulky information lives outside the main context, and only what is needed moves in and out. ```mermaid theme={null} flowchart LR SUB[Subagent
bulky raw data] -->|summary only| MAIN[Main agent
only what decisions need] SKILL[Skill
procedures and guidelines] -.->|only when needed| MAIN ``` Extending this idea of offloading — from the inside of one agent to a whole team of agents — gives the next two techniques: role division and parallel implementation. ### Divide roles along context boundaries The basic shape for using multiple agents is an orchestrator that forms the strategy, delegates tasks to specialized agents, and integrates what comes back. What matters is the criterion used to cut the roles. **Divide roles along context boundaries, not by type of work.** Splitting by stage — a gatherer, an analyst, an implementer — forces a context handoff at every stage boundary, and the coordination cost piles up. Draw the boundary between pieces of work that do not need each other's context instead, and each handoff shrinks to a summary. **✕ Divided by type of work** — every handoff transfers the whole context. ```mermaid theme={null} flowchart LR A[Gatherer] ==>|hand over everything| B[Analyst] ==>|hand over everything| C[Implementer] ``` **○ Divided along context boundaries** — each handoff is only a summary or a self-contained instruction. ```mermaid theme={null} flowchart LR S[Research agent] -->|summary only| ORCH[Orchestrator
analysis and judgment] ORCH -->|self-contained instructions| W1[Implementation agent A
feature and tests] ORCH -->|self-contained instructions| W2[Implementation agent B
feature and tests] ``` Applying that criterion gives the following division: * **Offload information gathering.** The raw data exploration produces is useless to later stages — the cleanest boundary there is. Have the agent return only a summary. * **Keep analysis and judgment with the orchestrator.** Weighing the agents' results against each other and deciding the next move can only happen where all the results converge. * **Group work that needs the same context.** Splitting work that rests on the same understanding — a feature and its tests — across agents means building the same context twice. The same principle shows up in the delegation instructions. Hand each agent a self-contained package: the objective, the output format, the tools to use, and the task boundary. Delegate with fuzzy boundaries and agents duplicate each other's work or leave gaps nobody picks up. Keep the number of parallel agents to a handful. Every additional agent adds integration cost, and past a point that cost outweighs the gain from parallelism. ### Implement in parallel **Issue × Worktree** is context-boundary division applied to implementation work. A well-decomposed sub-issue is a unit of work complete within its own context: a parent issue holds dependency-linked sub-issues, and git worktrees isolate each agent by directory so one issue's changes never touch another's. Task granularity determines whether the agent succeeds. For the criteria — how small each task should be and how to validate a split with independence and revert checks — see [Task Breakdown](/development/task-breakdown); for how to structure a parent issue with sub-issues and set the dependencies between them, see [Turning tasks into issues](/development/task-breakdown#turning-tasks-into-issues). Responsibilities are sharply separated between two agent roles — an orchestrator that analyzes and judges, and Workers that implement: * **Team Lead (orchestrator).** Builds the dependency graph, decides execution layers, creates and assigns tasks, runs the merge gate between layers, and synchronizes Workers. It does **not** write code. * **Worker (implementer).** For its assigned issue, it creates a worktree, sets up the environment, implements, self-reviews, opens a pull request, reports back, and cleans up its worktree. Workers and worktrees map one to one. The Lead splits the sub-issues into **layers** based on their dependencies. Issues that do not depend on each other share a layer, giving a two-level structure: parallel within a layer, sequential between layers. ```mermaid theme={null} flowchart TB LEAD[Team Lead
splits issues into layers by dependency] LEAD -->|1. assigns Workers| L0 L0 ==>|2. every PR merged| LEAD LEAD -->|3. assigns Workers| L1 subgraph L0["Layer 0 (no dependencies)"] direction LR A[Worker A
implements Issue A] B[Worker B
implements Issue B] C[Worker C
implements Issue C] end subgraph L1["Layer 1 (depends on layer 0)"] direction LR D[Worker D
implements Issue D] E[Worker E
implements Issue E] end ``` Within a layer, one Worker per issue implements in its own worktree at the same time, each opening its own pull request. Synchronizing between layers is the Lead's job: it waits for the whole layer to merge, notifying a human about anything still pending, then refreshes the default branch before starting the next layer — so later work always builds on the latest code. A dependency cycle is an error: detect it, report it, and stop. ## Responsibility boundaries The premise of every technique on this page is that **responsibility for AI-generated code rests with the human**. Delegating to AI does not transfer that responsibility; what changes is where the human's attention goes. The dividing line is simple: **delegate what can be verified mechanically, and keep what requires judgment.** Checks whose correctness is decided by matching against rules are what AI does well, while judgments whose right answer depends on context can only be made by a human. Areas that can be verified mechanically against rules. * Compliance with coding conventions, naming, and type definitions * Presence and coverage of test code * Bug and security scanning (only high-confidence findings reported) Areas where the right answer depends on context and requires judgment. * Whether the change meets the business requirement * Soundness of architecture and design decisions * Security risk acceptance and the final merge judgment The effect of this split is a change in what human review time is spent on. With conventions and naming already checked by AI, humans spend their time on the judgment only they can make: whether the change actually meets the requirement. Agentic workflows delegate more than [vibe coding](/ai/vibe-coding) does, but they inherit its groundwork: if interactive collaboration does not work yet, put the foundation in place before delegating whole implementations. # Skills Source: https://lib.findy.co.jp/ai/skill A skill packages a workflow into a directory of SKILL.md and supporting files that an AI agent loads on demand, so the same procedure runs reproducibly. ## Overview A skill is a reusable unit of workflow knowledge for an AI agent, defined as a directory holding a required `SKILL.md` file and optional supporting files. The `SKILL.md` encodes the procedure: step-by-step guidance, a fixed output format, and the decision points that genuinely need a human. Once written, it is invoked as a command or triggered automatically, and produces similar results no matter who runs it. This page explains the principles behind skills, their structure, and how to create and invoke one. ## Design principles A skill exists to give an agent the context it otherwise lacks for real work: the domain expertise and procedures specific to your organization. Its design rests on four principles. Capture your organization's procedural knowledge in a version-controlled form, so the agent works from supplied context instead of guesswork. Turn multi-step tasks into consistent, auditable procedures that run the same way no matter who invokes them. Write a skill once and use it with any skills-compatible agent or product. Only the `name` and `description` load at startup; the body and supporting files load only when they become relevant. ## Anatomy of a skill A skill lives in its own directory (in Claude Code, `.claude/skills//`) and consists of a `SKILL.md` file plus optional supporting files. ### Directory layout ```text Directory layout theme={null} .claude/skills/ └── commit-message/ ├── SKILL.md # Required. Frontmatter + instructions ├── references/ # Optional. Read only when the body links to them │ └── semantic_versioning.md └── scripts/ # Optional. Executed, not loaded into context └── check_format.sh ``` | Part | Role | | ------------- | ---------------------------------------------------------------------------------------------------------------------------- | | `SKILL.md` | The required file, holding the frontmatter that identifies the skill and the instructions for the agent. | | `references/` | Optional supporting documents — full rule tables, templates, long examples — linked from the body and read only when needed. | | `scripts/` | Optional utility scripts the body tells the agent to run. Only their output enters the context, not their source. | ### Structure of SKILL.md `SKILL.md` has two parts: YAML frontmatter that identifies the skill, and a Markdown body with the instructions and examples. ```md SKILL.md theme={null} --- name: commit-message description: Generate commit messages that follow semantic versioning rules. Use when committing changes or when asked to write a commit message. --- # Instructions 1. Inspect the staged diff and classify the change as major, minor, or patch. 2. Combine it with a type prefix (feat, fix, docs, ...) per references/semantic_versioning.md. 3. Output a single-line message in the form `-: `. ``` | Part | Role | | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | The identifier, kept matching the directory name — which is also where the invoking command (`/commit-message` in Claude Code) comes from. | | `description` | When the skill applies. The agent matches requests against this text, so it decides whether the skill triggers at all. | | `allowed-tools` | Optional. The tools the skill is allowed to use. An experimental field, so support varies between implementations; see "Keep tool permissions minimal" below for how to scope it. | | Body | The instructions the agent follows once the skill is active: steps, output format, examples. | ### Progressive disclosure A skill is loaded in stages rather than all at once. At session start the agent reads only the frontmatter of every installed skill. The body is read when a request matches the `description`, and files under `references/` are read only when the body points to them. Instructions that would otherwise sit permanently in a system prompt or an MCP tool definition therefore cost almost no context until the moment they are needed. ## Creating a skill ### Name and describe for discovery The `name` and `description` are the only parts loaded before the skill is chosen, so they carry the whole burden of discovery. * Name skills after the action, in lowercase and hyphens. Avoid vague names such as `helper` or `utils`. * Write the `description` in third person, state both what the skill does and when to use it, and include the words a user would actually say. ### Write for a capable reader The agent already has general programming knowledge, so include only what it cannot infer: your conventions, your constraints, your procedures. Concise instructions outperform thorough ones. * Keep the `SKILL.md` body under roughly 500 lines; move bulk material to `references/` files. * Keep references one level deep — every supporting file should be linked directly from `SKILL.md`, because nested reference chains get read partially. * Use one term per concept throughout (always "field", not a mix of "field", "box", and "element"). * Avoid time-sensitive statements ("before August, use the old API") that silently become wrong. * Heavy use of "MUST", "NEVER", and "ALWAYS" is a warning sign. Explaining why a rule exists works better than stacking prohibitions, because the agent can then judge unforeseen situations correctly. * Also state what to do off the happy path — a missing argument, an external service returning an error. When the instructions cover only the normal case, the behavior in exceptional cases varies from run to run. ### Provide a template for the output format When output consistency matters, provide a template for the output format and match its strictness to the task — anywhere from a loose example of the structure to an exact template the agent must always follow. * Where downstream processing or tools depend on the shape of the output — data formats, standardized reports — use a strict template and state that this exact structure must always be used. * Where the structure may reasonably vary with the content, present the template as a sensible default and note that it can be adapted to the situation. ```md A strict template (for report generation) theme={null} ## Report structure ALWAYS use this exact template structure: # [Analysis Title] ## Executive summary [One-paragraph overview of key findings] ## Key findings - Finding 1 with supporting data ## Recommendations 1. Specific actionable recommendation ``` ### Show with input/output examples Elements that resist description — style, tone, level of detail — are best conveyed as input/output pairs. Showing the desired output carries more reliably than describing it. ```md Examples in the body (for commit-message) theme={null} # Examples - Input: fix a typo in the README installation steps Output: `patch-docs: fix typo in installation steps` - Input: add JWT-based login to the auth service Output: `minor-feat: add JWT login to auth service` ``` When the output must always follow the same structure, use the template from the previous section instead of examples. Examples also consume context every time the body loads, so move long or numerous examples into a `references/` file that is read only when needed. ### Match freedom to fragility Decide how much latitude the instructions leave, based on how fragile the task is. Where many approaches are valid — a code review, an analysis — give heuristics and let the agent choose the route. Where the operation is fragile or must be consistent — a migration, a release step — give the exact command and state that it must not be modified. When several tools could work, name one default and mention an alternative only for the cases where the default fails. ### Keep tool permissions minimal Grant a skill only the tools its workflow actually uses. In Claude Code, declare them as `allowed-tools` in the frontmatter of `SKILL.md`: ```yaml theme={null} allowed-tools: Read, Glob, Bash(git status*), Bash(git diff*) ``` * Do not allow tools that never appear in the procedure. A skill that only reads has no need for Write or Edit. * Scope Bash per command: split a broad pattern like `Bash(git *)` into specific ones such as `Bash(git status*)` and `Bash(git diff*)`. * As a rule, do not allow destructive commands such as `rm` or `git push --force`. A skill that only provides knowledge and uses no tools needs no `allowed-tools` declaration at all. ### Build in verification Multi-step skills drift without checkpoints. Two patterns keep them on track: * **Checklists.** Have the skill emit its step list and check items off as it progresses, so skipped steps become visible. * **Feedback loops.** Pair each output with a validation step — a validator script, a lint run, a checklist review — and instruct the agent to fix and re-validate until it passes before moving on. ### Orchestrate sub-agents A skill with many steps runs more reliably as an orchestrator — the `SKILL.md` body directs the flow while independent subtasks are handed off to sub-agents — than as one long procedure executed by a single agent. * Run subtasks with no dependencies between them in parallel; per-dimension reviews or multi-area investigations have no reason to run in sequence. * Make each sub-agent's instructions self-contained. Sub-agents cannot see the calling conversation, so pass all necessary context in the prompt. * Always follow a parallel phase with a step that consolidates the results. * Spell out in the body how data crosses steps — which later step consumes the output of an earlier one. Keep the parallel count to a handful; beyond that, the cost of consolidation outweighs the gain from parallelism. ### Create with skill-creator skill-creator is a tool from Anthropic that supports writing a new skill. In Claude Code, install it from the official marketplace: ```text theme={null} /plugin install skill-creator@claude-plugins-official ``` Describing the skill you want starts an interactive flow. skill-creator confirms the purpose and the decisions the skill should surface, then scaffolds the directory — `SKILL.md`, `references/` files, and `scripts/` when needed — in the structure described earlier on this page, and cleans up files that turn out to be unnecessary. ## Invoking a skill A skill is invoked in two ways. Explicitly, as a command, or automatically, when the agent matches the request against the skill's `description`. Both paths run the same `SKILL.md`; the command is simply the direct handle. A skill is not limited to a single-shot task. Its body can drive a longer, interactive flow that asks clarifying questions, runs sub-agents in parallel, and calls other skills as steps. See [Vibe Coding](/ai/vibe-coding) for the collaboration basics that skills build on, and [Agentic Workflow](/ai/agentic-workflow) for the delegation model where multi-step skills run. # Vibe Coding Source: https://lib.findy.co.jp/ai/vibe-coding Building software in dialogue with an AI coding agent: the groundwork it requires, the verification that stays with the human, and the supporting techniques. ## Overview Vibe coding is a development approach in which you describe what you want in natural language, let a generative-AI coding agent produce the code, and steer, verify, and refine the result through continuous dialogue. The term is sometimes used for a throwaway-prototype style in which generated code is accepted without being read. This page covers something else: the practice as applied to a production codebase maintained by a team, where a human stays responsible for verifying the output. This page explains the groundwork a coding agent needs before it can perform, the verification responsibilities that stay with the human, and how to handle sessions, context, and custom instructions. ## Prerequisites A coding agent's output quality is bounded by the codebase and process it works in. When AI adoption fails to deliver, the cause may be missing groundwork rather than the tool. The coding agent has nothing consistent to imitate and no guardrails to catch its mistakes. Conventions, tests, and design knowledge are not made obsolete by the coding agent — they are what the coding agent runs on, and what lets you judge its output. Prepare the foundation before expecting results. A coding agent imitates the code it sees. A codebase that follows one consistent style — for example, a published style guide — steers generation toward uniform output, while mixed styles produce mixed results. Tests serve two roles: they are a primary source the coding agent reads to understand the spec, and a guardrail that mechanically catches wrong output before a human does. READMEs, design docs, and coding agent instruction files (such as `AGENTS.md` or tool-specific configuration) are standing context the coding agent receives every session without being pasted into the prompt. A habit of small, focused pull requests keeps coding agent output reviewable. Generation speed is worthless if the change arrives too large to verify. Dead or unused code works against all of the above: the coding agent cannot tell that it is obsolete and will imitate it. Removing it is part of preparing the codebase. ## Verification and the human's responsibility Vibe coding changes who writes the code, not who is responsible for it. The author of a generated change is the human who requested it: before sending it to review, be able to explain every line. Generation speed that produces unexplained changes does not raise productivity — it moves the cost from writing to reviewing, where it grows. Do not rely on eyeballing alone. Hand the coding agent a check that returns pass or fail — the test code, the build, a linter — and instruct it to iterate until the check passes. Then have the coding agent show evidence (the test output, the command it ran and its result) rather than assert success. ## Manage sessions deliberately Treat a session as a unit of context. Keep related changes in the same session so the coding agent retains shared context. Horizontal expansion is the clearest example: get a single instance working and verified, then expand to the remaining places in the same session, where the verified implementation is still visible to the coding agent. The proven example serves as the spec for the repetitive work. ```text Example instruction for the expansion theme={null} Apply the same pattern you just used for UserCard to ProductCard and OrderCard. ``` Start a new session for an unrelated task so stale history does not bleed in. A session's history has the same limitation as human short-term memory: the more irrelevant material it holds, the less reliably the relevant part is used. A working rule: after correcting the coding agent twice on the same issue, stop correcting. The context now holds the failed attempts, and they keep steering the output. Start a new session with a prompt that incorporates what the failures taught you — a clean session with a sharper prompt outperforms a long session with accumulated corrections. ## Keep course corrections cheap A coding agent generates code fast, which means it generates the wrong code fast too. To keep the cost of a wrong direction low, structure the work so any single step is cheap to discard, and commit at a fine grain. Commit in small, coherent steps — finer than you would open a pull request — so you always have a safe point to roll back to. Take a data-model change as an example: proceed in sequence — **define the model → verify it → generate the migration**. ```text Example sequence: adding a table theme={null} 1. Create only the model file and commit it 2. Verify the field names and fix mistakes (e.g. last_name → family_name) 3. Generate and run the migration ``` With this order, a wrong field name caught at step 2 costs a one-file fix. Catch it after generating the migration in one shot, and the correction requires a rollback and changes across multiple files. ## Hand over just enough context The largest factor in output quality is the size and focus of the context handed to the coding agent. A sprawling, multi-part request spreads the coding agent's attention thin and accuracy drops; a narrow task with the right references produces clean, on-target code. The goal: **give the coding agent the smallest context that is still complete enough to do the job.** ```text Bad: too much theme={null} Something's broken in search — fix it. Just in case, here's the full source of search.ts, query-parser.ts, and index.ts (about 1,500 lines), plus the Slack thread from a similar issue we had before. And if anything looks worth refactoring along the way, go ahead. ``` ```text Bad: not enough theme={null} Search is broken, fix it ``` ```text Good: just enough theme={null} Search sometimes returns a 500 error. Error log: 2026-07-07T10:23:45.812Z ERROR GET /api/search?q=tokyo cafe 500 (12ms) TypeError: Cannot read properties of undefined (reading 'split') at parseQuery (src/search/query-parser.ts:27:35) at searchHandler (src/api/search.ts:18:24) at Layer.handle [as handle_request] (node_modules/express/lib/router/layer.js:95:5) Write a failing test that reproduces it first, then fix it. ``` The first has enough information, but most of it is noise that dilutes attention, and the "refactor along the way" mixes in an unrelated concern. The second is minimal, but with no location and no detail about the symptom it invites an unbounded search. The third hands over only what acting on the task requires: the symptom, the actual error log, and how to proceed. It states no theory about the cause: the log carries the failing request and the failing location as facts, and tracing them to the cause — a keyword containing a full-width space — is the coding agent's job. Structure also matters. A hierarchically organized instruction — goal, then constraints, then concrete references — is parsed more reliably than one long undifferentiated paragraph. ```text A poor instruction theme={null} Add an API for updating a user's role. While you're at it, make roles editable from the UI too, make sure authorization is handled properly, and match the validation to the existing code — something reasonable is fine. ``` ```text Example of a structured instruction theme={null} Goal: add an API endpoint that updates a user's role Constraints: - Scope is PUT /api/users/:id/role only; no UI changes in this task - Use the existing authorization middleware References: - Mirror the routing and input validation of updateUser in src/api/users.ts - Use requireRole from src/middleware/authorize.ts for the permission check ``` When the task needs data the coding agent cannot see — issues, library documentation, design data — provide it through a Model Context Protocol (MCP) server rather than pasting large blobs into the chat. ## Use custom instructions Custom instructions are an instruction file the coding agent reads at the start of every session: build and test commands, style rules that differ from defaults, repository conventions, and known gotchas. They are the concrete form of the "documentation and custom instructions" item in the foundation above. ```markdown AGENTS.md theme={null} # Commands - Build: npm run build - Typecheck: npm run typecheck - Lint: npm run lint (auto-fix: npm run lint -- --fix) - Test a single file: npm run test -- path/to/file.test.ts (prefer over the full suite) - Dev server: npm run dev (http://localhost:3000) # Code style - Use ES modules (import/export), not CommonJS (require) - Use named exports; do not add default exports - Destructure imports when possible (e.g. import { foo } from 'bar') - New UI components follow the structure of src/components/Button/ (colocated test and story) # Testing - Test framework is Vitest; do not use Jest APIs - Place tests next to the source file as .test.ts - Avoid mocks where possible; use the fixtures in tests/fixtures/ # Repository etiquette - Branch naming: feature/- - Commit messages follow Conventional Commits (feat:, fix:, chore:) - Run npm run typecheck and npm run lint before opening a pull request # Architecture - All API access goes through src/lib/api-client.ts; never call fetch directly - State management is TanStack Query; do not introduce Redux # Environment quirks - The dev server requires .env.local; copy it from .env.example - Use Node 22 (managed with mise); npm install fails silently on Node 18 ``` Check the file into git so the team shares it, and keep it short — in an overlong file, individual rules get lost and adherence drops. Knowledge that is only sometimes relevant belongs in a [skill](/ai/skill), which loads on demand instead of occupying every session. ## Related pages Where vibe coding builds in dialogue, agentic workflows delegate implementation to coding agents and scale it in parallel. Encode a recurring workflow once as a skill and invoke it whenever needed. # CI/CD Source: https://lib.findy.co.jp/development/ci-cd What a CI/CD pipeline provides, how to keep CI fast, automate releases, notify results, and treat deployment frequency as a metric. ## Overview CI/CD is the practice of automating the steps between a code change and its delivery to users. Continuous integration (CI) builds and tests every change automatically; continuous delivery (CD) automates the path from a merged change to a deployed one. This page explains what a CI/CD pipeline provides, how to keep CI fast, how to automate releases, how to notify pipeline results, and how to treat metrics such as deployment frequency. Examples use GitHub Actions, but the principles apply to any CI service. ## What a CI/CD pipeline provides With a pipeline in place, the routine steps around every change run without manual intervention: Tests run on every pull request, and merging is blocked until they pass. A broken change is caught in minutes, not after release. Each environment deploys on a defined trigger — for example, staging when a pull request targets a release branch, production when it merges. These two capabilities are the baseline. The following sections cover the properties that determine how much value the pipeline delivers: speed, release automation, and feedback that reaches developers. ## Keep CI fast The duration of CI is a cost paid by every change. As a guideline, keep the pipeline that gates pull requests within about 5–10 minutes. ### Cache dependencies Installing dependencies from scratch on every run is pure overhead. Official setup actions can cache them keyed on the lockfile, so the cache is rebuilt only when dependencies actually change. ```yaml Node.js theme={null} name: Test on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - uses: actions/setup-node@v7 with: node-version: 22 cache: "npm" - run: npm ci - run: npm test ``` ```yaml Ruby theme={null} name: Test on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - uses: ruby/setup-ruby@v1 with: ruby-version: "3.3" bundler-cache: true - run: bundle exec rspec ``` The same idea extends beyond package installs: any expensive, reproducible setup — a prepared database schema, a build artifact — is a caching candidate. ### Skip runs that cannot affect the result A change that does not affect runtime behavior does not need the full test suite. `paths-ignore` skips the workflow when only the listed files changed: ```yaml theme={null} on: pull_request: paths-ignore: - "**.md" - "docs/**" ``` If the skipped workflow is a required status check, the check stays pending and blocks the merge. In that case, keep the workflow running and skip only the expensive jobs inside it, or adjust the required checks. ### Parallelize tests As the test suite grows, total execution time grows with it. Parallelization splits the suite across jobs that run concurrently. The `matrix` strategy runs the same job once per listed value; each job uses its value to execute only its own slice of the suite: ```yaml Node.js theme={null} jobs: test: runs-on: ubuntu-latest strategy: matrix: shard: [1, 2, 3] steps: - uses: actions/checkout@v7 - uses: actions/setup-node@v7 with: node-version: 22 cache: "npm" - run: npm ci - run: npx jest --shard=${{ matrix.shard }}/${{ strategy.job-total }} ``` ```yaml Rails theme={null} jobs: test: runs-on: ubuntu-latest strategy: matrix: ci_node_index: [0, 1, 2] env: RAILS_ENV: test steps: - uses: actions/checkout@v7 - uses: ruby/setup-ruby@v1 with: ruby-version: "3.3" bundler-cache: true # database setup steps omitted - name: Run tests for this shard env: CI_NODE_TOTAL: ${{ strategy.job-total }} CI_NODE_INDEX: ${{ matrix.ci_node_index }} run: | TEST_FILES=$(find spec -name "*_spec.rb" | sort | awk "NR % $CI_NODE_TOTAL == $CI_NODE_INDEX") if [ -n "$TEST_FILES" ]; then bundle exec rspec $TEST_FILES fi ``` The wall-clock time of a parallel run is the time of its slowest shard. If the runner has no shard option, or its default split leaves the shards unbalanced, split the file list yourself and distribute it by measured execution time — not by file count — so the shards finish together. Increase the shard count as the suite grows. ### Scale up runners Once caching and parallelization are in place, larger runners with more CPU cores and memory shorten builds and increase in-job parallelism. Cost per unit of run time rises with runner size, but a shorter run consumes fewer minutes, so the billed amount can stay flat or even drop while waiting time falls. Treat CI optimization as a measurement loop. Per-job timings from the CI logs show where the time actually goes; find the largest slice, change one thing, and verify the delta instead of optimizing on a hunch. ## Automate releases A manual release — writing the changelog and running deploy steps in the right order — is where mistakes and delays creep in. The whole sequence can be automated: Release notes are assembled from the history of merged pull requests and their commits. See [Commit messages](/development/pull-request#commit-messages) for how to write messages that read well later. Assemble the list of pull requests merged since the last release as the release notes, so nobody hand-edits a changelog. One workflow generates a release pull request. A separate workflow, triggered by the push to `main` when it merges, runs the deploy. A production release becomes a review and a merge instead of a runbook. The relationship between the branches is as follows: ```mermaid theme={null} gitGraph commit id: "init" branch develop checkout develop branch feature/a checkout feature/a commit id: "feat-a" checkout develop merge feature/a branch feature/b checkout feature/b commit id: "feat-b" checkout develop merge feature/b branch release-YYYY-MM-DD checkout release-YYYY-MM-DD commit id: "release" checkout main merge release-YYYY-MM-DD ``` For example, the following workflow implements this flow for a branch model where `develop` collects merged work and `main` mirrors production — a simplified form of git flow. Triggered manually, it cuts a dated release branch from `develop`, assembles the list of pull requests merged since the last release into the body, and opens a release pull request into `main`: ```yaml theme={null} name: Create Release Pull Request on: [workflow_dispatch] concurrency: group: create-release-pr cancel-in-progress: true jobs: create-release-pr: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 with: fetch-depth: 0 # use a PAT (or a GitHub App token) token: ${{ secrets.RELEASE_PAT }} - name: Ensure no pull request to main is open env: GH_TOKEN: ${{ secrets.RELEASE_PAT }} run: | test "$(gh pr list --base main --json number --jq length)" = "0" - name: Create a release branch from develop id: branch run: | branch="release-$(date +'%Y-%m-%d-%H%M%S')" git checkout -b "$branch" origin/develop git push -u origin "$branch" echo "name=$branch" >> "$GITHUB_OUTPUT" - name: List pull requests merged since the last release env: BRANCH: ${{ steps.branch.outputs.name }} run: | { echo "## Changes" git log --merges --pretty=format:'%s' "origin/main..${BRANCH}" \ | grep -oE '#[0-9]+' | sed 's/^/- /' } > body.md - name: Open the release pull request env: GH_TOKEN: ${{ secrets.RELEASE_PAT }} BRANCH: ${{ steps.branch.outputs.name }} ACTOR: ${{ github.actor }} run: | gh pr create --base main --head "$BRANCH" \ --title "Release $(date +'%Y-%m-%d')" \ --body-file body.md \ --assignee "$ACTOR" ``` The pull request list is extracted from merge commits, so this workflow assumes pull requests are merged into `develop` with "Create a merge commit". With squash merges, replace the extraction with a query such as `gh pr list --state merged`. A separate workflow triggers on the push to `main` — the merge of the release pull request — and runs the production deploy: ```yaml theme={null} name: Deploy on: push: branches: [main] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: deploy: runs-on: ubuntu-latest environment: production timeout-minutes: 30 permissions: contents: read id-token: write steps: - uses: actions/checkout@v7 - uses: actions/setup-node@v7 with: node-version: 22 cache: "npm" - run: npm ci - run: npm run build - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} aws-region: ap-northeast-1 - name: Deploy run: npm run deploy # platform-specific deploy command ``` `concurrency` prevents overlapping deploys when pushes land in quick succession. The `environment` designation applies GitHub's protection rules and environment-scoped secrets to production. The same approach removes recurring friction elsewhere in the workflow: assigning the author to their own pull request, applying labels based on branch name or changed paths, and extracting checked items from the pull request template as a QA hand-off at release time. ## Notify pipeline results A pipeline shortens the feedback loop only if its results reach the people who act on them. Two channels cover most cases: * **Status checks on the pull request.** The CI service reports each job's result on the pull request itself. This is built in and covers feedback while a change is under review. * **Chat notifications.** Events that happen outside a pull request — a production deploy finishing, a scheduled workflow failing — need a push channel such as Slack. For example, a step at the end of a deploy job can post the result to Slack whether the job succeeds or fails: ```yaml theme={null} steps: # deploy steps omitted - name: Notify Slack of the result if: always() uses: slackapi/slack-github-action@v3 with: webhook: ${{ secrets.SLACK_WEBHOOK_URL }} webhook-type: incoming-webhook payload: | text: "Deploy ${{ job.status }}: ${{ github.workflow }} (${{ github.ref_name }})" ``` Notify successes as well as failures. If only failures produce a message, silence is ambiguous — it can mean the deploy succeeded, or that the notification path itself is broken. A success message confirms that the pipeline and the notification channel are both working. ## Deployment frequency as a result, not a target Deployment frequency is a widely used productivity metric, and a high value is the *result* of working practices: automated pipelines, small well-scoped changes, and a test suite that keeps the risk of each change low. Optimizing the number itself inverts the causality and degrades the outcome it is meant to measure: * **Splitting unchanged work into more deploys** adds operational overhead and raises the change failure rate without delivering more value. * **Lowering deploy frequency to improve the failure rate** batches more changes into each deploy, which makes failures more likely and their causes harder to isolate. To raise deployment frequency sustainably, improve the inputs instead: keep CI fast, automate the release path as described above, and keep changes small and independently shippable (see [Task breakdown](/development/task-breakdown) and [Testing](/development/testing)). # Modifiability Source: https://lib.findy.co.jp/development/modifiability Classify software changes into three kinds — separate, revise, and remove — and learn the patterns that break modifiability and how to prevent them. ## Overview Modifiability — also known as changeability — is the property of software that lets it accept change at low cost and low risk. Software — user interfaces in particular — never reaches a finished state: designs are refined, feature requirements shift, and data-handling needs evolve, so the practical measure of a codebase is not how well it works today but how cheaply and safely it absorbs the next change. Change arrives from many directions at once: design changes, feature-requirement changes, and data-processing concerns such as performance. A structure that resists change turns each of those changes into a large, risky project; a structure built for change turns most of them into small, routine edits. Building and maintaining that structure is a core part of development work, not an optional polish step. This page defines modifiability by classifying changes into three kinds — **separate**, **revise**, and **remove** — and explains why the classification matters, the three patterns that break modifiability, and how to protect it through collaboration across roles. ## The three kinds of change Not every change stresses a codebase the same way. Classifying an incoming change by **what stays the same and what is altered** tells you which part of the structure the change relies on, and therefore what must be prepared in advance. | Kind | What stays the same | What changes | Typical examples | | -------- | -------------------------------------------------- | ---------------------------------------- | --------------------------------------------------------------------- | | Separate | The inputs and outputs of each unit | The arrangement, presentation, or volume | Moving content from a modal to a page; adding pagination | | Revise | The conceptual model behind the units | The representation of inputs or outputs | Adding an image to a card; adding an input field; changing sort order | | Remove | The behavior of everything around the removed unit | The unit itself disappears | Retiring a feature; deleting an obsolete screen | The following code — a list view for order history — serves as the running example for all three kinds. `OrderItem` owns the representation, `OrderList` assembles the list, and `OrderHistoryModal` is the container; each is a self-contained unit that receives its data from outside. ```tsx Running example: an order history list theme={null} // Everything below lives in the @order-history module. // One order. This unit owns the representation. type OrderItemProps = { order: Order }; export const OrderItem = ({ order }: OrderItemProps) => (
  • {order.title}
  • ); // The list of orders. Data comes in from outside. type OrderListProps = { orders: Order[] }; export const OrderList = ({ orders }: OrderListProps) => (
      {orders.map((order) => ( ))}
    ); // OrderList used inside a modal export const OrderHistoryModal = ({ orders }: { orders: Order[] }) => ( ); ``` ### Separate A change is a *separation* when the inputs and outputs of the affected units do not change — only their arrangement, presentation, or volume does. Moving content from a modal dialog to a dedicated page is a typical example. The data the content receives is the same, and the data it produces is the same; only the container changes. If the content is a self-contained unit that receives its data as input, the change amounts to relocating that unit — a matter of minutes, not days. ```tsx Separate: from a modal to a page theme={null} // The container changes from a modal to a page. // OrderList moves as-is — not a single line inside it changes. export const OrderHistoryPage = ({ orders }: { orders: Order[] }) => ( ); ``` Volume changes belong here as well. When a growing dataset requires pagination, the shape of each item does not change; only the quantity handled at a time does. A well-separated structure keeps quantity concerns isolated, so a change in data volume touches nothing except the code that manages quantity. Separations become cheap when units are divided along the **conceptual data model** — the essential entities of the domain — rather than along incidental boundaries such as the shape of an API response or the current layout. A unit built around a stable concept keeps receiving the same input no matter how its surroundings are rearranged. ### Revise A change is a *revision* when the representation of inputs or outputs changes: a card starts displaying an image, a form collects one more field, a list shows the newest entries first. A revision requires each affected unit to respond to the change. When units are separated along stable concepts, each response stays local: the unit that owns the changed representation adapts, and units that do not touch the changed representation stay untouched. ```tsx Revise: add a product image theme={null} // Only OrderItem — the unit that owns the representation — responds. export const OrderItem = ({ order }: OrderItemProps) => (
  • {/* added */} {order.title}
  • ); // OrderList and OrderHistoryPage stay untouched. // Order gains thumbnailUrl, so types and tests follow the change. ``` What makes revisions safe is **change detection** — the ability to see, cheaply and mechanically, where the effects of a change reach: When types are derived from a single source of truth (SSOT), changing a definition surfaces every affected location as a type error, before the code ever runs. Tests that assert observable behavior fail exactly where a revision alters behavior, turning "what did this break?" into a mechanical question. See [Test Code](/development/testing). A revision that fails to fail anything deserves suspicion: either the change had no observable effect, or the detection net has a hole. Noticing that a test *should* have failed — and fixing the test — is part of making revisions safe. ### Remove A change is a *removal* when a mechanism or its representation is deleted outright. Deleting code is easy; anyone can do it. The hard part is doing it **safely, quickly, and simply** — deleting one thing must not break an apparently unrelated part of the system. Whether a removal takes minutes or days of tracing hidden dependencies is decided long before the removal happens, by how the code was structured when it was written. ```diff Remove: retire the order history feature theme={null} // routes.tsx — remove the usage and the import. // With its entry point no longer imported, the @order-history // module — OrderList and OrderItem included — can be deleted whole. - import { OrderHistoryPage } from "@order-history"; export const routes = ( } /> - } /> ); ``` Designing for removal means keeping dependencies explicit and one-directional, so that the question "what depends on this?" has a mechanical answer. Code whose dependents cannot be enumerated cannot be deleted with confidence, and code that cannot be deleted permanently raises the cost of every future change. ## Why the classification matters The classification is useful because each kind of change relies on a different structural preparation, and because it turns "make the code easy to change" from a slogan into concrete design decisions. **Each kind demands its own preparation.** Separations rely on unit boundaries that follow the conceptual data model. Revisions rely on change detection — types and tests that surface the reach of a change. Removals rely on explicit, enumerable dependencies. A codebase can be strong in one and weak in another, so "is this easy to change?" is really three questions. **Misclassification wastes effort in both directions.** Treating a separation as a revision leads to rewriting logic that only needed relocating. Treating a revision as a separation ships a behavior change without updating the tests and types that should have tracked it. Naming the kind of change first keeps the response proportional. For example, the earlier "modal to page" change ends up looking quite different depending on how it is handled: ```tsx Treating a separation as a revision theme={null} // Two ways to handle "move the modal content to a page" // ❌ Treated as a revision: the list rendering is rewritten for the page // (the same view now exists twice, and a later revision such as // adding a product image must be paid in both places) export const OrderHistoryPage = ({ orders }: { orders: Order[] }) => (
      {orders.map((order) => (
    • {order.title}
    • ))}
    ); // ✅ Treated as a separation: OrderList moves to the new container as-is export const OrderHistoryPage = ({ orders }: { orders: Order[] }) => ( ); ``` **The classification is a design tool.** When writing new code, the question "which future changes should be separations, which revisions, and which removals?" produces concrete decisions: where to place unit boundaries, which types to derive rather than re-declare, which dependencies to keep explicit. How types are declared illustrates the difference preparation makes. The two declarations below implement the same behavior, but respond very differently when the definition they depend on changes: ```typescript Re-declared vs. derived types theme={null} // greeting.ts type GreetingArgs = { name: string }; export const greeting = ({ name }: GreetingArgs) => `Nice to meet you, ${name}!`; // message.ts // ❌ Re-declares the same shape by hand // (when greeting's definition changes, this type silently keeps the old shape) type MessageArgs = { name: string } & { agent: Agent }; // ✅ Derives the input type from greeting // (a change to greeting's definition propagates to this type // and its callers as type errors) type MessageArgs = Parameters[0] & { agent: Agent }; export const message = ({ name, agent }: MessageArgs) => `Hello, I am ${agent.name}. ${greeting({ name })}`; ``` Suppose `greeting` later requires a new field. With the hand-copied type, `MessageArgs` silently keeps its old shape: only the internal call to `greeting` errors, the fix stays local, and callers of `message` never learn that the concept they depend on has changed. With the derived type, the requirement propagates into `MessageArgs`, and every caller of `message` fails to compile — the full reach of the change is visible immediately. The dependency exists either way; deriving the type is what makes it **visible to the compiler**, and therefore detectable. ## Three patterns that break modifiability Even a structure built for change has limits. When changing well-structured code still hurts, the cause is usually one of three patterns — and all three originate not in the code but in how changes are decided and communicated. ### Broken premises A structure is built on an agreed conceptual model: which entities exist, how data is grouped, where the boundaries lie. A *broken premise* is a change that invalidates that model itself, or an implementation that violates the agreed decomposition after the fact. For example, suppose the agreement was "group the data by date, with each group holding entries of kind and value," and the delivered data instead groups by kind, holding per-date values inside. Every unit boundary that was drawn from the agreed model is now drawn in the wrong place, and the cost is not one revision but a cascade of them. Conceptual models are the foundation that separations and revisions stand on. Aligning on them — and treating a change *to the model itself* as a consultation with room for pushback, rather than a notification — is what keeps the foundation stable. ### Inconsistency *Inconsistency* is when the same kind of representation or the same data concept is specified differently from one place to the next, or when a specification flips back and forth — changed, reverted, then changed again. Each inconsistent variant needs its own structure and its own change-detection wiring, so the cost of the machinery that makes change cheap is paid repeatedly for what is conceptually one thing. Modifiability is a means of converging on a better product through iteration; it is not a mechanism for absorbing decisions that were never really made. The prevention is consistency and record-keeping: reuse the established pattern for an established concept, and when a decision is deliberately revisited, record what changed and why, so the codebase converges instead of oscillating. ### Unannounced changes Humans are poor at detecting differences. Feature-level changes get examined from every angle — risk, feasibility, compatibility — but a small visual or behavioral change, such as centering a piece of text, feels risk-free to the person requesting it and slips through without being announced. The asymmetry has a structural cause: functional requirements have obvious failure modes, so they attract scrutiny; non-functional details do not, so they reach implementation without ever being examined. But an unannounced change is invisible to every safety net — no test is updated, no reviewer knows what to look for, and the change itself is recorded nowhere. The prevention is to make every change explicit: communicate it when requesting it, leave it in a written log, and describe it in the pull request that implements it, so the change is visible to reviewers and to anyone reading the history later. ## Keeping modifiability across roles Modifiability cannot be maintained by the implementing team alone. The three breaking patterns all arise at the boundaries between roles — between design and implementation, between API providers and consumers, between the person requesting a change and the person making it. Protecting modifiability is therefore a collaboration protocol, not just a coding standard. Align on the conceptual data model — the entities and their grouping — before code is written on either side of a boundary. Routine changes within the model need only communication; a change that breaks the model warrants prior consultation, with the consumer holding a real option to push back. Give an established concept one specification and reuse it. When a decision is revisited, record the new decision and its reason in a place both sides can see, so the same discussion is not repeated and the codebase does not oscillate. Announce changes when requesting them, however small they seem, and describe the resulting behavior change in the pull request. Splitting work into small, independently revertible units keeps each change explainable — see [Pull Requests](/development/pull-request) and [Task Breakdown](/development/task-breakdown). Keep types derived from their single source of truth and keep tests asserting observable behavior, so the reach of any change — including one requested by another role — surfaces mechanically. See [Test Code](/development/testing). A useful default posture at role boundaries: routine changes are welcome as long as they are communicated; only changes that destroy an agreed concept require negotiation before they happen. This keeps the cost of ordinary collaboration low while protecting the foundation everything else depends on. ## Related pages Keep each change small, explainable, and reviewable — the unit in which changes are made explicit. Split work into independently revertible units so every change stays cheap to undo. Build the change-detection net that makes revisions safe. Ship changes progressively so even large changes stay reversible. # Pull Requests Source: https://lib.findy.co.jp/development/pull-request Pull request basics and practice: setting up a PR template, standardizing commit messages and titles, judging granularity, and keeping granularity right. ## Overview A pull request (PR) is the mechanism for proposing changes to a repository and collaborating on them through activities such as review. Changes are proposed through branches: a PR is opened from the head branch, which contains what to apply, into the base branch, which is where the changes are applied, so that only approved changes are merged. ## Commit messages A commit message is a summary of the changes contained in a commit. Standardizing how messages are written as a team convention makes it possible to trace, in a consistent format, which commit in the history changed what. The [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) format is widely used for this convention: `: `, with a prefix that indicates the kind of change. | Type | Use for | Example message | | ---------- | --------------------------------------------------------- | ---------------------------------------------------------- | | `feat` | Adding a feature | `feat: add notification settings to the profile page` | | `fix` | Fixing a bug | `fix: correct the boundary handling in the date filter` | | `docs` | Documentation-only changes | `docs: add setup instructions` | | `refactor` | Restructuring code without changing behavior | `refactor: extract date formatting into a shared function` | | `test` | Adding or updating tests | `test: add boundary-value test cases` | | `chore` | Build configuration, dependency updates, and other upkeep | `chore: update dependencies` | [Semantic Versioning](https://semver.org/) is sometimes used together with Conventional Commits. A version takes the form `MAJOR.MINOR.PATCH`, where the version level to bump is decided by the compatibility of the change. It is sometimes adopted in development where compatibility must be managed, such as libraries and APIs. Combining the two makes the change content easier to understand from the commit message and lets the release version be determined mechanically. | Version level | Use for | Version example | | ------------- | ------------------------------------- | ----------------- | | `MAJOR` | Backward-incompatible changes | `1.4.2` → `2.0.0` | | `MINOR` | Backward-compatible feature additions | `1.4.2` → `1.5.0` | | `PATCH` | Backward-compatible bug fixes | `1.4.2` → `1.4.3` | When combining the two, prefix the type with the version level, as in `-: `. This makes explicit the version level that the type alone does not determine: a feature addition with a breaking change becomes `major-feat`, and a small feature addition that does not affect compatibility becomes `patch-feat`. A commit message can consist of the one-line `: ` alone. To explain the background or reason for a change, add a body — and footers as needed — after a blank line. ```text Summary only theme={null} fix: correct the boundary handling in the date filter ``` ```text With a body theme={null} refactor: extract date formatting into a shared function The formatting logic duplicated across screens had drifted apart in three places, so it is consolidated into a shared function. ``` ```text With Semantic Versioning theme={null} major-feat: change the notification list API response to a paginated format Returning every notification in a single response cannot keep up with the growth of notifications, so the response is changed to a paginated format. BREAKING CHANGE: the top level of the response changes from an array to an object, so clients must read notifications from the items array. ``` ## Titles A PR title is a one-line summary of the change. Titles appear in PR lists, notifications, and the history after merging, so standardize a style that lets readers identify the change without opening the description. Base the title on the commit messages, using the same Conventional Commits and Semantic Versioning formats. For a PR with a single commit, reuse the commit message as the title; for a PR with multiple commits, choose the type and summary that represent the change as a whole. ## Template A PR template is a Markdown skeleton that is automatically inserted into the description field when a PR is created. With a template in place, the structure of the description — overview, changes, verification — stays consistent across PRs, and the information reviewers need is codified as a rule in advance. Add `.github/PULL_REQUEST_TEMPLATE.md` to the repository. Lay out the information reviewers need, separated by headings and comments. ````markdown theme={null} ## Overview ## Background / Purpose ## Changes -
    Prompt used ### LLM model used ### Prompt ```markdown ```
    ## Test plan - [ ] Added or updated unit tests - [ ] Confirmed all existing tests and CI pass ## How to verify - [ ] Exercised the affected feature locally and confirmed it behaves as expected - [ ] Confirmed surrounding features in the affected area still work ## Related issue ## Notes ````
    The template takes effect only once it exists on the default branch. PRs created after the merge get the skeleton inserted into their description automatically.
    Review the template regularly. Remove sections nobody fills in. If there are items that come up often as questions in review, add them as sections. ## Draft A draft is a PR state that signals the work is not yet ready for review. While a PR is a draft it cannot be merged, so unfinished changes are never pulled in by accident. Opening a PR as a draft before the work is finished lets you gather feedback on the direction of the implementation and design early, over the diff of the actual code. Unlike aligning through documents or conversation, the discussion is grounded in working code, so you and the reviewers reach a shared understanding at an early stage. Once the feedback you need is in and the understanding is aligned, you can either continue working on the same PR, or close the draft and open a new PR from scratch. The cost of changing direction grows as the work progresses, so a draft is an effective way to turn back early and keep the sunk cost to a minimum. Select "Create draft pull request" when opening the PR, and switch it out of the draft state with "Ready for review" once it is ready. ## Granularity Pull request granularity is whether a single PR concentrates on one thing — the uniformity of its change content. It is measured by whether the changes are uniform, not by the number of lines or files in the diff. ### The difference between size and granularity Size (lines and files changed) and granularity (uniformity of the change content) are different measures, and granularity is the one that drives the split decision. The test is whether the PR's change content is uniform — the PR concentrates on one thing — not how large the diff is. | Example change | Size | Appropriate? | Reason | | ----------------------------------------------------------------- | ------------ | ------------- | ------------------------------------------------- | | Bulk rename of a function used in 10,000 places | Large | Appropriate | The change content is uniform, so one PR is fine | | Data fetching, transformation, and rendering implemented together | Small–medium | Inappropriate | Different kinds of changes are mixed, so split it | | A 20-line bug fix plus an unrelated "while I'm here" refactor | Small | Inappropriate | Two different changes are mixed, so split it | ### Why granularity matters A focused diff narrows the scope that has to be reviewed. For the same total amount of work, reviewing one change ten times is lighter for both the author and the reviewer than reviewing ten changes in one PR. A diff with uniform change content fits in a reviewer's head, so review stops being deferred and finishes in minutes. Reverting a PR with a single, uniform change rolls back exactly that change and nothing else. Short-lived branches are less likely to run into conflicts. A PR with uniform change content also has a narrow impact area, so isolating the cause during an incident is fast. A large, mixed PR produces a negative cycle: it raises the reviewer's cognitive load, review is deferred, the branch lives longer, conflicts accumulate, and the next review is harder still. Granularity also matters for context management when AI agents do the work. Having an agent handle multiple requirements or unrelated changes in the same context spreads its attention and lowers precision. Splitting the work into PR units with uniform change content lets the agent focus its limited context on one thing, raising the accuracy of both implementation and review. ### Tips for getting granularity right #### Break work down first Decompose the work into independently workable tasks before writing code, so each task maps to one PR. See [Task Breakdown](/development/task-breakdown). #### When in doubt, split smaller When unsure about the right granularity, choose the smaller one. Discussing granularity within the review exchange aligns expectations with the reviewer. Splitting a PR that has already grown large is harder than starting small and coarsening the granularity later. When in doubt, ship small and flesh the work out incrementally. #### Review first Finer granularity creates more moments where the next piece of work cannot start until a merge lands. If review stalls there, it becomes the bottleneck and slows down overall development speed. So when a review request arrives, handle it before your own work. The context switch of interrupting your own work may be a concern, but reviewing a well-granulated PR takes minutes — sometimes under a minute — so the cost of the interruption is small. Keeping granularity right is what lets the review-first habit take root in the team. #### Keep CI fast Finer granularity means more PRs, and CI runs grow in proportion. If CI stays slow, the accumulated waiting before each merge erodes development efficiency instead. Aim for runs under 10 minutes, and treat anything over 5 minutes as slow enough to improve. For concrete techniques, see [Keep CI fast](/development/ci-cd#keep-ci-fast) in CI/CD. #### Separate deploy from release Treating the deploy of code and the release of a feature as separate events lets unfinished work merge without waiting for the whole feature to be done, so PRs can keep shipping small. See [Release](/development/release-strategy) for details. # Refactoring Source: https://lib.findy.co.jp/development/refactoring Change structure without changing behavior: separate structure from behavior changes, proceed in small reversible steps, and design code for deletion. ## Overview Refactoring is changing the internal structure of code without changing its observable behavior. Structure drifts as a codebase absorbs change after change, and refactoring is the ongoing work that restores [modifiability](/development/modifiability) — the property that keeps the next change cheap and safe. Refactoring is not a special project or a phase that waits for permission: it can happen at any time, and it is always in demand. What needs discipline is not *when* to refactor but *how* — in units that a reviewer can verify, in steps that can be undone, and toward a structure whose parts can eventually be deleted. This page covers three practices that provide that discipline — separating structure changes from behavior changes, proceeding in small reversible steps, and designing for deletion — and the role of test code that underpins all of them. ## Structure changes and behavior changes Every change to code belongs to one of two categories, and the two are verified in entirely different ways. | | Structure change | Behavior change | | ------------------ | ------------------------------------------------------------------------ | ------------------------------------- | | What changes | Names, boundaries, dependencies, duplication — how the code is organized | What the software observably does | | Who notices | Only developers reading the code | Users, API consumers, tests | | How it is verified | Existing tests pass, unchanged | Updated tests assert the new behavior | | Risk profile | Low, and mechanically checkable | Carries product risk by definition | The examples below use a common running example: a hook that fetches order history and shapes it for display. ```tsx Running example: fetching and shaping order history theme={null} // Fetch order history and shape it for display export const useOrderHistory = () => { const { data } = useOrders(); // Hide cancelled orders and sort newest first const orders = (data ?? []) .filter((order) => order.status !== "cancelled") .sort((a, b) => compareDesc(a.orderedAt, b.orderedAt)); return { orders }; }; ``` Which category a change to this hook belongs to is decided by one thing: whether the list content — the observable behavior — changes. In a structure change, only the code arrangement changes and the hook returns the same list, so existing tests pass unchanged. ```tsx Structure change: extract the shaping logic into a function theme={null} const selectOrders = (data: Order[] | undefined) => (data ?? []) .filter((order) => order.status !== "cancelled") .sort((a, b) => compareDesc(a.orderedAt, b.orderedAt)); export const useOrderHistory = () => { const { data } = useOrders(); return { orders: selectOrders(data) }; }; ``` In a behavior change, the list content changes, so the tests are updated to the new behavior. ```tsx Behavior change: stop excluding cancelled orders theme={null} export const useOrderHistory = () => { const { data } = useOrders(); // Drop the filter and keep only the newest-first sort const orders = (data ?? []) .sort((a, b) => compareDesc(a.orderedAt, b.orderedAt)); return { orders }; }; ``` Refactoring, by definition, covers only the first kind — the structure change. The moment a change alters what the software does, it is no longer a refactoring — it is a behavior change that deserves a behavior change's scrutiny. ### Do not mix the two in one pull request A structure change and a behavior change must not share a pull request. The reason is how each is reviewed. A structure-only pull request is verified with a single question: is the behavior identical? The evidence is mechanical — no test was modified, and every test passes. A reviewer can approve a large structural diff quickly, because the tests answer the hard question. A behavior-change pull request is reviewed against its intent: the diff includes updated tests, and the reviewer checks that the new assertions match the specification. A pull request that mixes a structure change and a behavior change destroys both verification strategies. The tests changed, so "tests unchanged and passing" no longer proves behavior is preserved — the reviewer must classify every line by hand, asking of each whether it was supposed to change behavior or not. The review becomes slower and less reliable at the same time. Using the earlier extraction as the example, the two treatments differ like this. First, the pattern where the pull request only extracts: the tests are untouched, and passing unchanged is itself the evidence that behavior is preserved. Showing cancelled orders ships as a follow-up behavior-change pull request with its updated tests. ```tsx ✅ Implementation: this pull request only extracts theme={null} const selectOrders = (data: Order[] | undefined) => (data ?? []) .filter((order) => order.status !== "cancelled") .sort((a, b) => compareDesc(a.orderedAt, b.orderedAt)); export const useOrderHistory = () => { const { data } = useOrders(); // Calling the same logic as before keeps the list identical return { orders: selectOrders(data) }; }; ``` ```tsx ✅ Test: passes unchanged theme={null} // The tests are untouched — passing unchanged is the evidence // that the observable behavior is preserved test("returns order history, newest first", () => { const { result } = renderHook(() => useOrderHistory()); // cancelled orders are excluded from the list expect(result.current.orders).toEqual([delivered2, delivered1]); }); ``` Mixing the extraction (structure) and the change to the visible orders (behavior) in one pull request instead rewrites the test expectations in the same diff: "tests pass unchanged" is lost as evidence, and the reviewer classifies every line by hand. ```tsx ❌ Implementation: structure and behavior share one diff theme={null} const selectOrders = (data: Order[] | undefined) => (data ?? []) // Alongside the extraction, the filter is dropped so // cancelled orders are now shown .sort((a, b) => compareDesc(a.orderedAt, b.orderedAt)); export const useOrderHistory = () => { const { data } = useOrders(); return { orders: selectOrders(data) }; }; ``` ```diff ❌ Test: expectations rewritten in the same diff theme={null} test("returns order history, newest first", () => { const { result } = renderHook(() => useOrderHistory()); - // cancelled orders are excluded from the list - expect(result.current.orders).toEqual([delivered2, delivered1]); + // cancelled orders now appear in the list + expect(result.current.orders).toEqual([delivered2, cancelled1, delivered1]); }); ``` The revert unit degrades the same way. A pull request should be a unit that can be reverted alone — see [Task Breakdown](/development/task-breakdown). Reverting a mixed pull request throws away a correct structure change to undo a wrong behavior change, or the reverse. When a reviewer requests a refactoring on a behavior-change pull request, it does not have to be fixed inside that pull request. Accepting the point and doing it as a follow-up structure-only pull request keeps both changes verifiable. ### The timing is a choice, not a rule Structure changes and behavior changes go into separate pull requests, but their order in time is a genuine choice. * **Structure first** — when the current structure makes the coming behavior change awkward, reshape the code first. The behavior change that follows becomes smaller and easier to review. * **Structure after** — when the behavior change is urgent or the structural problem only becomes visible while working, ship the behavior first and clean up immediately after, leaving the area ready for the next change. * **Not now** — when the code is rarely touched, the cost of restructuring may never pay back. Leaving it alone is a legitimate decision. A practical basis for the choice is how often the code changes: structure that many future changes will pass through pays back quickly, and structure nobody revisits pays back never. Delivering observable behavior early also reduces uncertainty — polishing structure before the behavior is validated optimizes code that may not survive. ## Tests define "behavior unchanged" Refactoring and test code are inseparable. "Existing tests pass unchanged" — the way a structure change is verified — only works when the tests genuinely assert the observable behavior. In other words, the claim that a refactoring preserves behavior is only as strong as the tests that assert the behavior. Two signals during a refactoring deserve attention: * **A test had to change.** Either the change was not actually structure-only, or the test asserts implementation details rather than observable behavior. Both cases need fixing — the first in the diff, the second in the test. See [Test Code](/development/testing). * **No test failed while the logic was temporarily broken.** If an intermediate mistake produced no failing test, the safety net has a hole in the area the refactoring touches. ## Small, reversible steps The cost of a change grows faster than its size: a larger diff takes disproportionately longer to review, conflicts with more concurrent work, and hides mistakes better. Refactoring therefore proceeds as a series of small steps, each of which keeps the system fully working. The rhythm of a step is fixed: make one structural move, verify that everything still passes, and commit. Renames, extractions, and moves each land as their own verified step. A step that turns out wrong is undone by reverting one small commit, not by untangling it from a day of other edits. The same rule applies at the pull request level. Merge small structure-only pull requests into the default branch continuously, rather than accumulating them on a long-lived refactoring branch. A long-lived branch collects conflicts with every concurrent change, and its eventual merge is exactly the large, risky diff the small steps were meant to avoid. Keep each pull request small enough that a reviewer finishes it in minutes. A restructuring too large for a single safe step is done as a **parallel change**: the new structure is built alongside the old, and traffic moves over incrementally. The three steps below walk through replacing a price formatter with a multi-currency one. Add the new structure next to the old one, with nothing using it yet. Behavior is untouched, so this step is trivially safe. ```tsx Expand: add the new function next to the old one theme={null} // The existing function is untouched export const formatPrice = (price: number) => `¥${price.toLocaleString()}`; // Add the new function; nothing uses it yet export const formatMoney = ({ amount, currency }: Money) => `${amount.toLocaleString()} ${currency}`; ``` Move callers from the old structure to the new one, a few at a time. Each migration is a small, independently revertible pull request, and the system works at every point in between. ```diff Migrate: move callers to the new function one by one theme={null} - {formatPrice(order.total)} + {formatMoney({ amount: order.total, currency: order.currency })} ``` When nothing uses the old structure, delete it. The refactoring is not finished until this step lands — a codebase carrying both structures is harder to change than before the refactoring started. ```diff Contract: delete the old function once nothing uses it theme={null} - export const formatPrice = (price: number) => - `¥${price.toLocaleString()}`; ``` The pattern works across ownership boundaries as well: renaming a field in an API response follows the same expand, migrate, contract sequence, with the provider and consumers moving in separate, individually safe steps. ## Designing for deletion Many refactorings end with a deletion: the contract step removes the old structure, an experiment that did not survive is removed, a feature flag whose rollout completed is retired — see [Release Strategy](/development/release-strategy). Deletion is where the classification of changes meets design: deleting code is easy, but deleting it **safely, quickly, and simply** is a property the code either was given when it was written or does not have — see [Modifiability](/development/modifiability). Code is easy to delete when its dependents can be enumerated mechanically and its parts live together. This is another way of saying the structure has high **cohesion** (code that changes together lives together) and low **coupling** (unrelated code does not depend on each other) — and the pair also indicates which direction a refactoring should move the structure. Cohesion has recognized levels of strength: the more essential the reason a module's parts are together, the stronger the cohesion. | Cohesion | Why the parts are together | Typical example | | ---------------------- | ----------------------------------- | ------------------------------------------------------------------------------ | | Functional (strongest) | They serve a single purpose | The UI, logic, and tests that make up one feature | | Sequential | One part's output feeds the next | A flow from fetch through transform to format | | Communicational | They operate on the same data | Operations reading and writing the same entity | | Procedural | They share only an execution order | Checks that run in sequence before a screen renders | | Temporal | They run at the same time | Initialization invoked together at startup | | Logical | They are the same kind of thing | A general-purpose function switching on a flag; utilities grouped only by kind | | Coincidental (weakest) | They happen to be in the same place | A miscellaneous `common` module | The priority is clear: aim for functional cohesion, accept sequential and communicational, and treat logical and coincidental cohesion as targets for decomposition. When the reason code is grouped shifts from "it changes together" to "it is the same kind" or "it happened to be there," that is the sign of weak cohesion. The following practices keep cohesion high and coupling low: * **Keep dependencies explicit and one-directional.** The question "what depends on this?" must have a mechanical answer — from types, imports, or module boundaries. Code whose dependents cannot be listed cannot be deleted with confidence. * **Group code by concept, not by kind.** When everything a feature needs lives in one subtree, deleting the feature is deleting the subtree. When it is scattered across shared layers, every deletion is an investigation. Places grouped only by kind — `utils`, `common` — are the typical form of logical or coincidental cohesion. * **Treat shared abstractions as deletion risks.** Every new caller of a shared helper makes that helper harder to remove or change. Extract shared code when duplication has proven itself a burden, not in anticipation. * **Delete dead code instead of keeping it.** Commented-out blocks, unused exports, and flags that never flip are not free: they mislead readers and dilute change detection. Version control already preserves everything — restoring deleted code is one revert away. Grouping by concept instead of by kind shows most clearly in the directory structure. ```text Grouped by kind vs. grouped by concept theme={null} # ❌ Grouped by kind: removing order history means # investigating every layer src/ components/OrderList.tsx hooks/useOrderHistory.ts utils/orderFormatters.ts # ✅ Grouped by concept: removing order history is # deleting one subtree src/ features/order-history/ OrderList.tsx useOrderHistory.ts formatters.ts ``` The forward-looking form of this practice is a question asked while writing new code: how will this be removed? A module that has an answer — its dependents are enumerable, its parts are together, nothing reaches into its internals — is cheap to delete, and for exactly the same reasons it is cheap to change. ## Related pages The classification of changes — separate, revise, remove — that refactoring keeps cheap. Split work into independently revertible units — the same principle that separates structure from behavior. The tests that make "behavior unchanged" a verifiable claim instead of a hope. Keep each pull request focused on one intent so review stays fast and reliable. # Release Source: https://lib.findy.co.jp/development/release-strategy Separate deploying code from releasing features. Covers environment-variable feature flags, trunk-based development, gradual rollout, instant rollback, and flag cleanup. ## Overview A release is the set of decisions about when and how a change becomes visible to users, independent of when its code reaches the production environment. The core principle is separating *deploy* from *release*: deploying is the mechanical act of shipping code, releasing is the decision to expose a feature. This page explains that separation, the feature flag mechanism that implements it, how it enables trunk-based development, and how to roll out, roll back, and clean up safely. The pipeline that automates deploys themselves is covered on the [CI/CD](/development/ci-cd) page; this page covers deciding when users see the result. ## Deploy and release are different events Deploying code and releasing a feature usually happen at the same moment, but they answer different questions and belong to different owners. Reflecting code in the production environment. A mechanical, repeatable step that can be automated and performed many times a day. Making a feature available to users. A judgment call about timing — coordination with announcements, support readiness, or business milestones. Keeping the two coupled forces a trade-off: either deploys wait for the business timing, so changes pile up into large risky batches, or features go live the instant their code lands, whether or not anyone is ready. Separating them is the foundation of continuous delivery — keeping the software releasable at all times, deploying small changes frequently, and choosing release timing deliberately. Small, frequent deploys improve speed and stability together, because each change is easier to review, test, and undo. ## Feature Flags A feature flag is a conditional that switches behavior on or off without a code change. The simplest reliable implementation is an environment variable read at one branch point in the code. In the following example, a new label is rendered only while the `FEATURE_NEW_LABEL` environment variable is `'true'`. Enable it locally to work on the feature, and merge with it disabled in the production environment. ```tsx React theme={null} export const SampleComponent = () => { const isEnabledNewLabel = process.env.FEATURE_NEW_LABEL === 'true'; return (
    {isEnabledNewLabel && NEW} Sample
    ); }; ``` ```ruby Ruby theme={null} def new_label_enabled? ENV['FEATURE_NEW_LABEL'] == 'true' end def sample_labels labels = [] labels << 'NEW' if new_label_enabled? labels << 'Sample' labels.join(' ') end ```
    Enabling the feature in the production environment is a configuration change — add `FEATURE_NEW_LABEL=true` to the environment — and disabling it is removing that line. The flag turns "the code is in the production environment" and "the feature is visible" into independent facts. That unlocks: In-progress features merge into the main branch behind a flag, invisible to users, instead of aging in a long-lived branch. Deploy to the production environment many times a day while choosing when to release each feature. If a released feature misbehaves, turn the flag off. Recovery time is minimal. Enable a feature in the verification environment or for internal use first, while users in the production environment still see the old behavior. Environment-variable flags can be implemented with no extra infrastructure, which makes them a good first step. Adopting a flag management library or service is also worth considering. ## Trunk-based development Trunk-based development is the practice of integrating all work into the main branch in small increments, avoiding branches that live for days or weeks. Long-lived branches accumulate merge conflicts, hide integration problems until the end, and produce large pull requests that are hard to review. Feature flags are what make this practical for features that take more than one pull request to build: each increment merges as soon as it passes review, guarded by the flag, and the feature is enabled only when the last piece lands. The result is that several people can build in the same area with conflicts less likely, and every merge is small enough to review properly. In the following diagram, one feature is split into three increments, each merged into the base branch with the flag off, and the release happens by enabling the flag after the last increment lands. ```mermaid theme={null} %%{init: {'gitGraph': {'mainBranchName': 'base'}}}%% gitGraph commit id: "released" branch increment-1 commit id: "work 1 (flag off)" checkout base merge increment-1 id: "PR: merge 1" branch increment-2 commit id: "work 2 (flag off)" checkout base merge increment-2 id: "PR: merge 2" branch increment-3 commit id: "work 3 (flag off)" checkout base merge increment-3 id: "PR: merge 3" commit id: "flag on = release" ``` This is the same principle as splitting work into small units — see [Task Breakdown](/development/task-breakdown) for how to slice the work and [Pull Requests](/development/pull-request) for keeping each merge reviewable. ## Isolate impact on the production environment with a topic branch flow Some changes cannot hide behind a flag — a visual overhaul or a framework migration, where the impact on the production environment cannot be hidden. The topic branch flow keeps granularity in that case. It is a three-layer branch structure that places a topic branch between the base branch and the working branches. Review and integration complete on the topic branch, and the base branch receives nothing until release, so small pull requests keep flowing without affecting the production environment. ```mermaid theme={null} %%{init: {'gitGraph': {'mainBranchName': 'base'}}}%% gitGraph commit id: "released" branch topic branch develop-1 commit id: "work 1" checkout topic merge develop-1 id: "PR: review 1" branch develop-2 commit id: "work 2" checkout topic merge develop-2 id: "PR: review 2" checkout base commit id: "other changes" checkout topic merge base id: "daily sync" checkout base merge topic id: "PR: release" ``` The flow works as follows. 1. Cut a topic branch from the base branch, then cut working develop branches from the topic branch. 2. Review on PRs from a develop branch into the topic branch. This is the stage where granularity is maintained. 3. When the work is ready for the production environment, merge a PR from the topic branch into the base branch. The review at this point can be minimal, because the changes were already reviewed in step 2. 4. Merge the base branch's changes into the topic branch at least once a day to keep conflicts to a minimum. The trade-off is that the topic branch itself is a long-lived branch: integration with the base branch is deferred, which is exactly what trunk-based development avoids, so the daily sync in step 4 is what keeps conflicts manageable. Prefer feature flags when the change can hide behind one, and reserve the topic branch flow for changes that cannot. ## Gradual rollout and rollback With deploy and release separated, releasing becomes a sequence of controlled steps rather than a single event. The code reaches the production environment disabled. Users see no change; the deploy itself carries near-zero release risk. Turn the flag on in the verification environment first. Problems surface before any user is exposed — the earlier a defect is found, the cheaper it is to fix. Enable the flag in the production environment at the chosen timing. If the rollout can be scoped, start with a specific group of users and widen the audience gradually. If the feature misbehaves, disable the flag. Rollback by flag only works if the old code path still exists and still passes its tests. Do not delete or break the fallback path while the flag is live. ## Test both states, then remove the flag A flag doubles the states your code can be in, and both states run in the production environment at some point in the feature's life. Write tests for each side of the branch: ```tsx theme={null} it('renders the NEW label when FEATURE_NEW_LABEL is true', () => { /* ... */ }) it('does not render the NEW label when FEATURE_NEW_LABEL is false', () => { /* ... */ }) ``` The off-state test is not optional: it is what guarantees the rollback path keeps working while the feature evolves. See [Test Code](/development/testing) for what makes these tests trustworthy. A flag is temporary by design. Once the feature has run stably with the flag on and there is no scenario for turning it back off, remove the flag, the dead branch, and the off-state tests in one pull request. Stale flags accumulate into a combinatorial maze of states nobody tests. A trustworthy test suite is what makes this deletion safe to do promptly. ## Related pages Slice features into increments small enough to merge behind a flag. Keep each merge small and reviewable. Tests that protect both flag states and make flag removal safe. # Task Breakdown Source: https://lib.findy.co.jp/development/task-breakdown Breaking work into small, independent units: benefits, good-task conditions, split-or-merge criteria, breakdown steps, dependencies, and issue creation. ## Overview Task breakdown is the practice of splitting a feature into small units of work before implementation. Each task aims to merge on its own, revert on its own, and drag no unrelated changes with it. The preparation done before writing code determines [pull request granularity](/development/pull-request#granularity): building PRs from the broken-down tasks lets you keep creating PRs at the right granularity. ## Benefits Listing and splitting tasks before starting looks like a detour, but across the development cycle it turns out to be the fastest path. Small, concrete tasks are easier to size than one large feature, so the total estimate carries fewer surprises. Writing the task list in an issue and having it reviewed before implementation surfaces missing cases and wrong assumptions while they are still cheap to fix, which cuts rework. Building PRs from the broken-down tasks keeps each PR focused on one thing and prevents it from bloating. A checklist of tasks makes status legible at a glance and makes handing work between people — or between people and AI agents — straightforward. Good task breakdown also translates into providing AI agents with the right context. Context engineering is the practice of preparing the context an AI agent works with — instructions, reference material, and scope — so that it contains exactly what the goal requires, no more and no less. A task with uniform change content lets you load only the information that piece of work needs, so the agent concentrates its limited context on one thing. Task breakdown, [pull request granularity](/development/pull-request#granularity), and context engineering are tightly linked: breakdown decides the unit of work, that unit drives the granularity of the PR, and that granularity determines the quality of the context the agent receives. Even when implementation is delegated to AI agents, the starting point is a good breakdown. ## What makes a good task A well-sized task satisfies four conditions at once. The change is safe to integrate on its own, and the application keeps working after the merge. Implementation and the tests that prove it ship in the same task, never separately. Rolling it back does not disturb other tasks. It stays small enough that a reviewer can read it through without breaks and understand the whole change. ## Keep together or split apart Cohesion decides the boundary. To judge whether two pieces of work belong to the same task, apply three checks in order. If any answer forces them together, keep them together. Does task B work correctly without task A? If not, they are one task. Is task A a meaningful, mergeable unit on its own? If not, merge it into the task that gives it meaning. Does reverting task A also break task B? If so, they are one task. Some combinations do not need the checks — they always land the same way. * Implementation and its tests * An API endpoint and its routing * A data model and its migration * Refactoring from new features * A library upgrade from feature work * A performance change from feature work * A data migration from feature work * Each stage of a feature flag (add → enable → remove) * Features that do not depend on each other ## Breakdown steps Breakdown is iterative. The first list does not need to be perfect; it gets more precise as more people look at it and as the breakdown progresses. This section walks through building a task list, using the task "Add a REST API that returns a list of users" as an example. Start by writing the rough requirement as an issue task list. A single top-level line is enough at this point. ```markdown theme={null} - [ ] Add a REST API that returns a list of users ``` Break the single line into pieces that ship on their own. Fetching the list, filtering, pagination, and sorting each became their own task. Build the feature by stacking small additions instead of implementing everything at once. ```markdown highlight={2-8} theme={null} - [ ] Add a REST API that returns a list of users - [ ] Fetch the list of users from the database and return it - [ ] Support filtering - [ ] id - [ ] name - [ ] Support pagination - [ ] Support sorting - [ ] id asc/desc ``` Have frontend, backend, and product reviewers look at the list before any code is written. Discussion turns vague items concrete: in this example, aligning in review pinned the name filter down to a partial match. ```markdown highlight={5} theme={null} - [ ] Add a REST API that returns a list of users - [ ] Fetch the list of users from the database and return it - [ ] Support filtering - [ ] id - [ ] partial match on name - [ ] Support pagination - [ ] Support sorting - [ ] id asc/desc ``` When implementation reveals reality, feed it back into the list and split further. In this example, writing tests surfaced the hidden requirement of handling deactivated users, and the fetch task split into "return all users" and "exclude deactivated users". Keep updating and splitting the list during implementation instead of letting one task swell. ```markdown highlight={3-4} theme={null} - [ ] Add a REST API that returns a list of users - [ ] Fetch the list of users from the database and return it - [ ] Return all users - [ ] Exclude deactivated users from the list - [ ] Support filtering - [ ] id - [ ] partial match on name - [ ] Support pagination - [ ] Support sorting - [ ] id asc/desc ``` ## Dependencies and parallelism Once the list settles, map the dependencies between tasks and identify which tasks can run in parallel. Tasks with no dependency on each other can be implemented simultaneously — by different people, or by multiple AI agents. For example, a bulk user registration feature breaks into five tasks: the domain model comes first, the parser and the validator depend only on it and run in parallel, the service layer combines them, and the API endpoint sits on top. ```markdown theme={null} - [ ] Add bulk user registration - [ ] Task 1: Define the domain model - [ ] Task 2: Implement the parser - [ ] Task 3: Implement the validator - [ ] Task 4: Implement the service layer - [ ] Task 5: Add the API endpoint ``` Mapping the dependencies of this task list produces the following diagram. ```mermaid theme={null} flowchart LR T1[Task 1
    Domain model] --> T2[Task 2
    Parser] T1 --> T3[Task 3
    Validator] T2 --> T4[Task 4
    Service layer] T3 --> T4 T4 --> T5[Task 5
    API endpoint] ``` ## Turning tasks into issues Once the breakdown and its dependencies are settled, create an issue per task. Checkboxes in a task list cannot track assignees, discussion, or progress per task. Promoting a task you are about to start into its own issue makes one issue one unit of work, with the requirements and discussion for that task collected in one place. Structure the feature and its tasks as parent and child issues. * **Parent issue** — represents the whole feature. It keeps the background, the goal, and the full task list, and links to each child issue so progress can be followed from one place. * **Child issue** — represents one broken-down task. It states the task's requirements and completion criteria, and records the dependencies mapped earlier. If the tool supports linking parent and child issues, the children's progress rolls up to the parent. On GitHub, this structure maps to two features: sub-issues and issue dependencies. Sub-issues carry the parent–child relationship — which feature a task belongs to — while dependencies carry the starting order — which task to start after. **Sub-issues (parent–child)** — adding the child issues to the parent as sub-issues makes the feature-task hierarchy explicit, and the children's completion rolls up to the parent as progress. See [Adding sub-issues](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/adding-sub-issues) in the GitHub Docs for the steps. For the bulk registration feature, the five child issues hang under the parent issue. Sub-issues (0 / 5) * [ ] Task 1: Define the domain model * [ ] Task 2: Implement the parser * [ ] Task 3: Implement the validator * [ ] Task 4: Implement the service layer * [ ] Task 5: Add the API endpoint **Dependencies (starting order)** — record the starting order between child issues as issue dependencies. Setting a dependency on Task 1 for the Task 2 issue makes it visible, even in lists, that Task 2 cannot start until Task 1 completes, and it can replace writing dependencies in the issue body. See [Creating issue dependencies](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/creating-issue-dependencies) in the GitHub Docs for the steps. The dependencies of the bulk registration feature form the following graph, where each arrow points to the issue it depends on — the one that must complete first. ```mermaid theme={null} flowchart RL T2[Task 2
    Parser] -->|depends on| T1[Task 1
    Domain model] T3[Task 3
    Validator] -->|depends on| T1 T4[Task 4
    Service layer] -->|depends on| T2 T4 -->|depends on| T3 T5[Task 5
    API endpoint] -->|depends on| T4 ``` Start with Task 1, which has no dependencies; Task 2 and Task 3 share the same dependency and can proceed in parallel. An issue created this way is a unit of work you can hand to a person or an AI agent as is. With the requirements, completion criteria, and dependencies in one place, the quality of the context the implementer receives is largely decided at this point. Issues with no dependencies can be started in parallel. # Test Code Source: https://lib.findy.co.jp/development/testing Write test code that protects the system — tests that pass when they should pass and fail when they should fail — instead of chasing coverage numbers. ## Overview Test code verifies that the system behaves as intended, and it is the safety net that makes continuous change possible. What matters in test code is that it **protects the system**: a protective test passes when the behavior is correct and fails when the behavior breaks. Its value is not in every test passing, but in preventing unintended behavior before it is released to production. This page explains the principles behind such tests and shows patterns for writing protective tests. ## Principles ### The purpose of tests is sustainable growth of the software Automated tests exist to make the growth of the software sustainable: it keeps the project in a state where the effect of a change can be verified with trustworthy results in a short time, so the team can keep changing the system with confidence. Detecting unintended behavior is the mechanism, not the end goal. Quality and speed are therefore not a trade-off. Skipping tests buys a short-lived head start, and the cost returns quickly as unintended behavior slipping in, manual re-verification, and fear of touching existing code. It is not that there is no time to write tests — there is no time because tests are not written. Refactoring and incremental improvement require confidence that existing behavior still holds after a change, and test code provides it. This matters even more when AI coding tools propose changes: whether a proposed change is safe to accept is ultimately answered by the test code, and the quality of the test code determines how much of that work can be delegated. ### Passing and failing must both be trustworthy Test code protects the system only while its results can be trusted in both directions. Two kinds of failures erode trust in test code: The behavior is broken but the test passes. Fragmentary assertions or mocking the logic under test cause it; the test reports safety that does not exist, and unintended behavior is released to production. The behavior is correct but the test fails. Dependence on implementation details or unstable timing causes it; frequent false positives train the team to ignore failures, until the moment the behavior truly breaks is missed as well. Strengthen assertions to reduce false negatives, and assert observable behavior rather than internal implementation to reduce false positives. Test code earns trust by keeping both low. The patterns later in this page guard mainly against false negatives. ### Coverage is a result, not a goal Coverage measures which lines test code executes, not whether it asserts the right behavior. A line can run without any assertion that it does the correct thing, so a high percentage can coexist with tests that protect very little. The reliable order is to decide **what behavior must not break**, then add test cases that would catch it breaking. Coverage rises as a consequence. ### Write tests in the same pull request as the implementation A behavior change and the test that protects it belong in the same pull request. Deferring the test to a follow-up leaves a window where the behavior is unprotected, and in practice the follow-up competes with new work and loses. Review test code at least as rigorously as implementation code: a weak assertion approved today is unintended behavior missed later. See [Pull Requests](/development/pull-request) for how to keep such changes reviewable. ## Writing tests that protect The following patterns are cases where a test looks fine but would not catch the bug it is meant to catch — that is, sources of false negatives. Each pattern strengthens the test until it fails for the failures that matter. The same patterns are the recurring weaknesses of test code generated by AI tools: generated tests tend to optimize for a green run rather than for protection, so review them against these patterns before merging, with the same scrutiny as hand-written tests. ### 1. Assert execution state, not just execution Asserting only that a callback was called hides three bugs: a handler that fires twice when it should fire once, a handler called with the wrong arguments (another user's data, for example), and an error-path callback that runs when it should never run. Pin down the count and the arguments, and assert that the handlers that should never be called were never invoked. ```javascript Jest theme={null} // ❌ Only asserts the callback was called // (double fire, wrong payload, and onError all pass) expect(onSuccess).toHaveBeenCalled(); // ✅ Pin the count, the arguments, and the silent paths expect(onSuccess).toHaveBeenCalledTimes(1); expect(onSuccess).toHaveBeenCalledWith({ userId: 123 }); expect(onError).not.toHaveBeenCalled(); ``` ```ruby RSpec theme={null} # ❌ Only asserts the callback was called # (double call, wrong payload, and on_error all pass) expect(on_success).to have_received(:call) # ✅ Pin the count, the arguments, and the silent paths expect(on_success).to have_received(:call).with(user_id: 123).once expect(on_error).not_to have_received(:call) ``` ### 2. Assert the whole expected value, not fragments Checking one field of a result leaves every other field unprotected: an unchecked field that is dropped, renamed, or corrupted passes silently. Compare the entire structure against a literal expected value, so any unintended change to the shape or content of the result fails the test. ```javascript Jest theme={null} // ❌ Only asserts individual fields expect(result.id).toBe(1); expect(result.name).toBe("Alice"); expect(result.role).toBe("member"); // ✅ Pin the whole structure (any unintended change fails) expect(result).toStrictEqual({ id: 1, name: "Alice", role: "member" }); // ✅ Check the keys in addition to individual fields // (missing or extra keys — dropped or unintentionally added fields — also fail) expect(Object.keys(result).sort()).toEqual(["id", "name", "role"]); expect(result.id).toBe(1); expect(result.name).toBe("Alice"); expect(result.role).toBe("member"); ``` ```ruby RSpec theme={null} # ❌ Only asserts individual attributes expect(result[:id]).to eq(1) expect(result[:name]).to eq("Alice") expect(result[:role]).to eq("member") # ✅ Pin the whole structure (any unintended change fails) expect(result).to eq({ id: 1, name: "Alice", role: "member" }) # ✅ Check the keys in addition to individual attributes # (missing or extra keys — dropped or unintentionally added fields — also fail) expect(result.keys).to contain_exactly(:id, :name, :role) expect(result[:id]).to eq(1) expect(result[:name]).to eq("Alice") expect(result[:role]).to eq("member") ``` Generated tests in particular often assert just enough for the current behavior to pass: the tests pass today, but there is no guarantee against a future extension that breaks an unasserted part of the behavior. ```javascript Jest theme={null} // ❌ Asserts only the count (any two items pass) expect(result.items.length).toBe(2); // ✅ Pin the whole expected value (any broken item, price, or total fails) expect(result).toStrictEqual({ items: [ { id: 1, name: "Coffee", price: 500 }, { id: 2, name: "Beans", price: 1200 }, ], total: 1700, }); ``` ```ruby RSpec theme={null} # ❌ Asserts only the count (any two items pass) expect(result[:items].length).to eq(2) # ✅ Pin the whole expected value (any broken item, price, or total fails) expect(result).to eq({ items: [ { id: 1, name: "Coffee", price: 500 }, { id: 2, name: "Beans", price: 1200 } ], total: 1700 }) ``` Loose matchers weaken assertions the same way. `toBeTruthy` and `be_truthy` pass for a non-empty string, a number, or any other truthy value, so a refactor that quietly changes a boolean return into another type keeps passing. Assert the exact value and type instead. ```javascript Jest theme={null} // ❌ Passes even when the value is not true ("yes" or 1 also passes) expect(active).toBeTruthy(); // ✅ Assert the exact value and type expect(active).toBe(true); expect(count).toBe(0); ``` ```ruby RSpec theme={null} # ❌ Passes even when the value is not true ("yes" or 1 also passes) expect(active?).to be_truthy # ✅ Assert the exact value and type expect(active?).to be true expect(count).to eq 0 ``` ### 3. Mock the boundary, not the logic under test When a green run becomes the goal, an AI coding tool may mock the very logic under test, or adjust the expected value to whatever the implementation currently returns. Such a test passes even if the implementation is wrong, because the implementation itself was treated as the answer key. The proper role of a mock is the opposite: fixing an external boundary — the current time, a network response — so that the output becomes deterministic. Output that depends on the current time, such as a mail body that embeds an expiry date, can only be asserted in fragments until the clock is pinned; once the time is mocked, the whole expected value can be asserted exactly, as in pattern 2. Describe how mocks should be used in each case in the project's custom instructions, and verify during review that the expected values come from the specification, not from the output. ```javascript Jest theme={null} // Implementation under test: builds a mail body whose expiry date // is seven days from the current time const mailService = { createInviteMailBody(toEmail, inviteLink) { const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); const expiryDate = expiresAt.toISOString().slice(0, 10); // e.g. "2026-07-08" return `Dear ${toEmail},\n${inviteLink}\nThis link expires on ${expiryDate}.`; }, }; const expectedBody = `Dear ${toEmail},\n${inviteLink}\nThis link expires on 2026-07-08.`; // ❌ Mocks the logic under test itself // (only asserts the mock's return value — a broken implementation passes) jest.spyOn(mailService, "createInviteMailBody").mockReturnValue(expectedBody); expect(mailService.createInviteMailBody(toEmail, inviteLink)).toBe(expectedBody); // ✅ Mock only the external boundary (the clock), run the real logic // (the expiry is pinned to 2026-07-08 — a bug in the implementation fails) jest.useFakeTimers().setSystemTime(new Date("2026-07-01T00:00:00Z")); expect(mailService.createInviteMailBody(toEmail, inviteLink)).toBe(expectedBody); ``` ```ruby RSpec theme={null} # Implementation under test: builds a mail body whose expiry date # is seven days from the current time class MailService def create_invite_mail_body(to_email, invite_link) expiry_date = (Time.zone.now + 7.days).strftime("%Y-%m-%d") # e.g. "2026-07-08" "Dear #{to_email},\n#{invite_link}\nThis link expires on #{expiry_date}." end end expected_body = "Dear #{to_email},\n#{invite_link}\nThis link expires on 2026-07-08." # ❌ Mocks the logic under test itself # (only asserts the mock's return value — a broken implementation passes) allow(mail_service).to receive(:create_invite_mail_body).and_return(expected_body) expect(mail_service.create_invite_mail_body(to_email, invite_link)).to eq(expected_body) # ✅ Mock only the external boundary (the clock), run the real logic # (the expiry is pinned to 2026-07-08 — a bug in the implementation fails) travel_to Time.zone.local(2026, 7, 1) do expect(mail_service.create_invite_mail_body(to_email, invite_link)).to eq(expected_body) end ``` ## Related pages Ship the test in the same reviewable unit as the change it protects. Delegate implementation to AI tools on top of a suite you trust. # Findy Library Source: https://lib.findy.co.jp/index A documentation site where Findy, Inc. shares its knowledge on a wide range of topics. Findy Library is a documentation site where Findy, Inc. openly shares its knowledge on a wide range of topics. Content is added and updated on an ongoing basis. We publish this knowledge in the hope of raising the bar for the industry as a whole, and of helping anyone who is stuck on the same problems we have faced. Please review the [Terms of Service](/terms-of-service) before using this site. ## Get started Findy Library serves the same content in two forms from a single source. Pick how you want to read it. Pick a category from the navigation tabs and read on. Every page offers a language switcher (English / 日本語). [Pull Requests](/development/pull-request) is a good place to start. Every page is also delivered as plain Markdown for AI agents. See the Quickstart for how to access it. [Continue to the Quickstart →](/quickstart) ## Find what you need | What you want to know | Where to look | | ------------------------------------------------------------------ | --------------------------------------------- | | How to write pull requests that are easy to review | [Pull Requests](/development/pull-request) | | How to break work into small, independent units | [Task Breakdown](/development/task-breakdown) | | How to write tests that protect the system | [Test Code](/development/testing) | | Building software in dialogue with an AI coding agent | [Vibe Coding](/ai/vibe-coding) | | Delegating planning, implementation, and verification to AI agents | [Agentic Workflow](/ai/agentic-workflow) | | Packaging workflows so any agent can reproduce them | [Skills](/ai/skill) | New topics are added over time. Check back regularly, or point your AI agent at the [Quickstart](/quickstart) to keep up with the latest content. # Quickstart Source: https://lib.findy.co.jp/quickstart How an AI agent reads Findy Library: the llms.txt index, per-page Markdown, and the search MCP server. ## Overview Findy Library is published so that AI agents can read it directly, not only people. Every page is available as plain Markdown, and the whole site is indexed for agents. This page describes the three access paths an agent uses. ## 1. Discover what exists `llms.txt` is an index that lists every page with a short description, so an agent can decide what to read before fetching anything else. `llms-full.txt` contains the entire site as a single file. ```bash theme={null} curl https://lib.findy.co.jp/llms.txt # index of all pages curl https://lib.findy.co.jp/llms-full.txt # the entire site in one file ``` ## 2. Read a page as Markdown Append `.md` to any page path to get its raw Markdown. The response is the same content people see, without HTML to scrape. ```bash theme={null} curl https://lib.findy.co.jp/development/pull-request.md ``` ## 3. Search with the MCP server Findy Library exposes a search MCP server at `https://lib.findy.co.jp/mcp`, which lets an MCP-capable agent query the docs directly instead of fetching pages one by one. No authentication is required. Register the server with your MCP client: ```bash Claude Code theme={null} claude mcp add --transport http findy-library https://lib.findy.co.jp/mcp ``` ```json Cursor (mcp.json) theme={null} { "mcpServers": { "findy-library": { "url": "https://lib.findy.co.jp/mcp" } } } ``` ```json VS Code (.vscode/mcp.json) theme={null} { "servers": { "findy-library": { "type": "http", "url": "https://lib.findy.co.jp/mcp" } } } ``` Once connected, ask a question and the agent retrieves the passages most relevant to it through the search tool. # Terms of Service Source: https://lib.findy.co.jp/terms-of-service The terms of service governing the use of Findy Library, the documentation site provided by Findy Inc. The Findy Library Terms of Service (the "Terms") set forth the conditions for using "Findy Library" (the "Service"), a documentation site provided by Findy Inc. (the "Company"). Any person who uses the Service (a "User") is deemed to have agreed to these Terms. ## Article 1 (Description of the Service) The Service publishes and provides documents and content on software development and other technical domains created by the Company (including text, diagrams, code examples, and all other information; collectively, the "Content"). ## Article 2 (Scope of License) The Company grants Users, on the condition that they comply with these Terms, a royalty-free right to view and consult the Content as reference information for personal or internal business use. This license does not constitute an assignment or transfer of any copyright or other intellectual property rights in the Content (see Article 7). ## Article 3 (Prohibited Acts) In using the Service, Users must not engage in any of the following acts: 1. Reproducing all or part of the Content and providing it to third parties through redistribution, republication, sale, or any other means, whether or not the Content has been modified 2. Presenting the Content as if it were created by a party other than the Company, or causing others to misidentify it as such 3. Using scraping, crawling, or other automated means to obtain the Content exhaustively or in bulk (excluding use by AI agents as provided in Article 4) 4. Interfering with, or creating a risk of interference with, the operation of the Service, the provision of the Content, or any other business of the Company 5. Violating laws, regulations, or public order and morals 6. Any other act that the Company reasonably determines to be inappropriate in light of the purpose of the Service ## Article 4 (Use by AI Agents) The Service does not prevent AI agents or other programs from referring to the Content as an information source and using it to generate answers or other output, and the Company anticipates such use. However, when providing answers or other output generated through such use to third parties, the User shall endeavor to indicate, to the extent practicable, that the Content originates from "Findy Library." If such use involves any act falling under the items of Article 3, it constitutes a violation of these Terms. ## Article 5 (Attribution) When quoting, referencing, or reprinting the Content, Users shall clearly indicate the name "Findy Library" as the source. ## Article 6 (Use of Access Analytics Tools) The Service may use Google Analytics or other access analytics tools to understand usage of the Service. These tools may use cookies or similar technologies to collect information about usage in a form that does not identify individuals. ## Article 7 (Intellectual Property Rights) Copyrights and other intellectual property rights in the Content belong to the Company or to third parties who have provided content to the Company. The license granted under Article 2 does not constitute an assignment or transfer of these rights. ## Article 8 (Disclaimer) 1. The Company makes no warranty whatsoever as to the accuracy, completeness, usefulness, currency, or fitness for a particular purpose of the Content. 2. The Company shall not be liable for any damage incurred by Users or third parties as a result of the use of the Content (including use by AI agents), except where such damage is caused by the willful misconduct or gross negligence of the Company. 3. The Company may change the content of the Service or suspend or terminate the provision of the Service without prior notice, and shall not be liable for any damage incurred by Users as a result. ## Article 9 (Response to Violations) 1. If the Company determines that a User has violated any item of Article 3, the Company may demand that the User cease the violating act, delete posted materials, or take other necessary measures, and may also seek injunctive relief, claim damages, and take other legal action. 2. If the Company determines that a violation described in the preceding paragraph has occurred or is likely to occur, the Company may suspend the provision of all or part of the Service without prior notice. ## Article 10 (Amendment of the Terms) The Company may amend these Terms without obtaining the individual consent of Users when the Company deems it necessary, by notifying Users through its official website or by any other method the Company reasonably determines to be appropriate. If a User uses the Service after the amended Terms have taken effect (regardless of the method of use, including whether or not via the website), the User is deemed to have agreed to the amendment. ## Article 11 (Governing Law and Jurisdiction) These Terms are governed by the laws of Japan. If a lawsuit arises between the Company and a User, the Tokyo Summary Court or the Tokyo District Court, depending on the amount in dispute, shall have exclusive jurisdiction as the court of first instance. ## Article 12 (Publication of the Terms) Because the Service has no member registration or other procedure that identifies Users, the Company does not conduct individual consent procedures for these Terms. The Company informs Users of these Terms by placing a link to them in the footer of the Service website and by publishing them on content pages within the Service. If a User uses the Service (regardless of the method of use, including whether or not via the website), the User is deemed to have agreed to these Terms. ## Article 13 (Severability) Even if any provision of these Terms or any part thereof is determined to be invalid or unenforceable under laws or regulations, only that provision or part shall be invalid, and the remaining provisions of these Terms shall remain in full force and effect. ## Supplementary Provisions Established and effective as of July 21, 2026.