When working on enterprise applications, developers regularly modify existing modules, add API endpoints, change database schemas, and introduce new business rules.
A task and a pull request help track these changes, but are they enough?
Not always. A developer joining the team six months later should be able to understand how a module works, how an API should be consumed, and why an important architectural decision was made without reading hundreds of historical pull requests.
The goal is not to document every line of code. It is to maintain the knowledge required to understand, operate, and safely change the application.
1. Understand the Different Types of Documentation
Enterprise teams typically maintain several complementary records.
| Record | Purpose | Example |
|---|---|---|
| Task / ticket | Explains what needs to change and why | Jira ticket describing an order cancellation feature |
| Commit | Records a code change in version control | Add order cancellation policy |
| Pull request (PR) | Explains and reviews the implementation | Changes to the order service and its tests |
| Technical documentation | Explains how the current system works | Order lifecycle, module responsibilities |
| API documentation | Defines the API contract | Parameters, response schema, errors |
| Architecture Decision Record (ADR) | Captures why a significant technical decision was made | Why Redis was selected for caching |
| Changelog / release notes | Describes changes included in a release | New cancellation feature and bug fixes |
| Runbook | Explains operational procedures | Steps to investigate failed background jobs |
These records solve different problems. A PR preserves the history of a change, while technical documentation helps developers understand the current system.
GitHub supports pull request templates to standardize descriptions, testing notes, and links to related issues. GitHub documentation
2. Do We Need to Document Every Code Change?
No. Documentation should be proportional to the impact of the change.
Example: Small bug fix
Suppose you fix a rounding issue in a discount calculation.
You might only need:
- A ticket describing the defect.
- A clear PR explaining the correction.
- An automated regression test.
A separate document would probably add unnecessary overhead.
Example: New payment workflow
Suppose you introduce a new payment authorization and refund workflow.
You may need to update:
- Module documentation describing the payment lifecycle.
- API documentation if endpoints or response contracts change.
- Tests covering authorization, refunds and failure scenarios.
- An ADR if you introduce a significant architectural decision.
- Operational documentation if deployments or reconciliation procedures change.
Rule: Document information that future developers, API consumers, operators, or support teams will need. Do not duplicate information that is already obvious from the code, tests, or an existing source of truth.
π 3. Where Should Documentation Live?
For many Rails projects, keeping technical documentation in the repository makes it easy to review and version alongside code.
An example structure:
rails_react_app/
βββ app/
βββ spec/
βββ db/
β βββ migrate/
βββ docs/
β βββ architecture/
β β βββ overview.md
β βββ decisions/
β β βββ 0001-use-redis-for-cache.md
β βββ modules/
β β βββ orders.md
β β βββ payments.md
β βββ api/
β β βββ openapi.yaml
β βββ runbooks/
β βββ deployment.md
β βββ failed-jobs.md
βββ .github/
β βββ pull_request_template.md
βββ README.md
βββ Gemfile
This is an example, not a mandatory Rails convention. A small project might need only a README and an API specification. Larger systems may use a dedicated documentation platform, especially when documentation is intended for external customers.
The important requirement is that documentation has a clear owner, a discoverable location, and a process for keeping it current.
π 4. What Should the Development Workflow Look Like?
Consider a new feature that allows customers to cancel eligible orders.
Step 1: Create the ticket
The ticket should explain the business requirement and acceptance criteria.
Feature: Cancel an eligible orderRequirements:- Customers can cancel orders before shipment.- Shipped orders cannot be cancelled.- Successful cancellation updates the order status.- The customer receives a confirmation.
This describes the expected behavior, not the implementation details.
Step 2: Implement the feature
The developer updates the appropriate Rails models, service objects, controllers, jobs, and tests.
For example:
class Orders::Cancellation
def initialize(order:)
@order = order
end
def call
return false unless @order.pending?
@order.update!(status: :cancelled)
end
private
def order
@order
end
end
The example illustrates a business operation; production code should also handle authorization, concurrency, and any payment or inventory side effects required by the business rules.
Step 3: Open a pull request
Link the ticket and explain the important changes.
## Summary
Adds order cancellation for eligible orders.
## Implementation
- Adds cancellation business logic.
- Updates order status.
- Adds regression tests.
## Tests
- [x] RSpec service specs
- [x] Request specs
- [ ] E2E tests, if required
## Impact
- [ ] API contract changed
- [x] Business rules changed
- [ ] Database migration added
## Documentation
- [x] Updated order lifecycle documentation
The template reminds the author to consider documentation without requiring a new document for every PR.
Step 4: Review and merge
The reviewer checks the code, tests, business rules, and any documentation affected by the change. CI validates the required checks before merging.
GitHub also supports protected branches, required status checks, and code ownership rules. GitHub PR management
Step 5: Release and maintain the record
If the feature affects customers, release notes may describe the new capability. If it changes operational procedures, update the runbook.
The ticket and PR remain historical records, while maintained documentation explains the current behavior.
5. How Should APIs Be Documented?
API documentation is especially important when the backend serves a separate React frontend, mobile app or other services.
Suppose Rails exposes:
GET /api/products?page=2&per_page=20
Consumers should know the supported parameters, defaults, response format, error behavior, authentication requirements, and compatibility expectations.
For shared APIs, OpenAPI is a strong default. It defines a machine-readable API contract that can support documentation generation, client generation, and contract validation. See the OpenAPI Specification.
A simplified example:
openapi: 3.1.0
info:
title: Products API
version: 1.0.0
paths:
/api/products:
get:
summary: List products
parameters:
- name: page
in: query
schema:
type: integer
minimum: 1
default: 1
- name: per_page
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 20
responses:
"200":
description: Products returned successfully
"401":
description: Authentication required
The specification must match the actual Rails endpoint. Ideally, keep it under version control and update it in the same PR when a public API contract changes.
Do not rely solely on comments in controllers or undocumented JSON responses.
6. When Should You Use an Architecture Decision Record?
An Architecture Decision Record (ADR) captures the reasoning behind a significant technical choice.
For example, your team decides to use Redis to cache frequently requested product searches.
The code explains how caching was implemented, but may not explain why Redis was selected instead of database caching or in-memory caching.
An ADR can record that reasoning:
# ADR-0001: Use Redis for Product Search Caching
## Status
Accepted
## Context
Product searches generate repeated database queries.
## Decision
Use Redis to cache eligible search results.
## Alternatives
- Database materialized views
- Process-local memory caching
## Consequences
- Reduced database load on cache hits
- Additional Redis infrastructure
- Cache invalidation must be handled correctly
ADRs should be used for meaningful decisions involving architecture, trade-offs, security, reliability, or maintainability, not minor refactoring.
Google’s Architecture Decision Records guidance provides additional examples and recommendations.
ποΈ 7. What About Database Changes and Deployments?
This is an important area that documentation practices can easily overlook.
Rails migrations already provide a version-controlled history of schema changes. For example:
class AddCancelledAtToOrders < ActiveRecord::Migration[7.2]
def change
add_column :orders, :cancelled_at, :datetime
end
end
The migration records the database change. You usually do not need a separate document repeating the migration.
However, significant database changes may require deployment instructions, data backfills, rollback considerations, or coordination between application versions.
For example, adding a non-null column to a very large production table may require a staged rollout rather than a single migration.
Document these operational concerns in the PR, deployment plan, or runbook as appropriate. Rails’ Active Record Migrations guide explains how migrations evolve the database schema.
8. Keep Documentation Useful Over Time
Documentation becomes a liability when it disagrees with the actual code.
Follow these practices:
- Documentation as code: Version technical documentation with the application where practical.
- Update with the change: Review documentation in the same PR as the behavior it describes.
- Assign ownership: Use code owners or named teams for critical module documentation.
- Avoid duplication: Maintain one authoritative API specification rather than multiple manually maintained copies.
- Automate validation: Check Markdown links, API specifications, and generated documentation where practical.
- Protect sensitive information: Never publish credentials, personal data, or internal security details in documentation.
- Review periodically: Remove obsolete instructions and update operational procedures.
For customer-facing APIs or regulated systems, additional requirements may include formal approvals, versioned release documentation, audit trails, and explicit API deprecation policies.
π― Final Takeaway
A well-run enterprise development process does not require a separate document for every code change. It requires each type of information to have an appropriate, discoverable record.
Remember:
- Ticket: What problem are we solving?
- Pull request: What did we change, and how did we verify it?
- Technical documentation: How does the system work today?
- API specification: How should consumers interact with the API?
- ADR: Why did we make this important technical decision?
- Release notes and runbooks: What was released, and how do we operate it?
The best documentation workflow is one in which maintaining the right information is part of completing a change, not a separate task that developers postpone until later.