This document outlines development conventions for the Codexis ebook conversion system, following decisions documented in Architecture Decision Records (ADRs).
Table of Contents
- Architecture Decisions
- Code Organization
- Elixir Conventions
- Phoenix Conventions
- Error Handling
- UI and Styling
- Performance
- Security
- Testing
- Documentation
- Version Control
- Workflow Practices
- References
Architecture Decisions
- Document all architectural decisions using ADR format (ADR-0001)
- Store ADRs in
docs/architecture/decisions/ - Use numeric prefixes with leading zeros (e.g., 0019-error-recovery-mechanisms.md)
- Follow status indicators: Proposed, Accepted, Rejected, Superseded
- Reference related ADRs in code comments where applicable
Code Organization
project/
├── lib/ # Core application code
│ ├── codexis/ # Main application modules (Contexts, Schemas, Services)
│ └── codexis_web/ # Phoenix web interface code (Controllers, Views, LiveViews, Templates)
├── config/ # Configuration files (config.exs, dev.exs, etc.)
├── test/ # ExUnit tests
│ ├── support/ # Test helpers and shared setup
│ └── fixtures/ # Test data fixtures (e.g., using ExMachina)
├── priv/ # Private assets (e.g., static files, templates, Repo migrations)
├── assets/ # Frontend assets (JS, CSS)
└── docs-site/docs/ # Documentation
- Structure files according to Phoenix conventions (contexts, controllers, views, etc.).
Elixir Conventions
General Style
- Follow community Elixir style guide, enforced by Credo.
- Write concise, idiomatic Elixir code.
- Prefer functional programming patterns and leverage immutability.
- Prefer higher-order functions and recursion over imperative loops where clear.
- Use pattern matching and guards extensively for function clauses.
- Utilize the pipe operator (
|>) for clear data transformation pipelines. - Apply the DRY (Don't Repeat Yourself) principle but prioritize clarity.
Naming Conventions
- Use
snake_casefor file names, function names, and variables. - Use
PascalCasefor module names.
Syntax and Formatting
- Follow the Elixir Style Guide (enforced by Credo and
mix format). - Prefer single quotes (
') for charlists and double quotes (") for strings (binaries).
Modules and Functions
- Organize code into modules following domain boundaries (ADR-0011).
- Prefer pure functions where possible.
- Use context structs (plain maps or structs) to pass related data through functions.
- Inject dependencies via function arguments or configuration.
- Use descriptive variable and function names (e.g.,
user_signed_in?,calculate_total).
OTP Practices
- Implement supervision trees for fault tolerance (ADR-0017).
- Use
GenServerfor managing stateful processes and background jobs. - Utilize
Taskfor concurrent, isolated jobs and asynchronous operations. - Implement circuit breakers (
Fuse) for external service calls where appropriate.
Phoenix Conventions
- Follow Phoenix conventions and best practices for contexts, schemas, controllers, views, etc.
- Leverage Phoenix's built-in functions and macros.
- Use Ecto effectively for database operations and schemas.
- Follow RESTful routing conventions where applicable.
- Use Phoenix contexts for organizing related business logic and data access.
Error Handling
Elixir/OTP Philosophy
- Embrace Elixir's "let it crash" philosophy where appropriate, relying on supervisors for recovery (ADR-0017).
- Use
{:ok, value}and{:error, reason}tuples for function return values indicating success or recoverable errors. - Leverage the
withspecial form for handling sequences of operations that can fail. - Define custom exception structs for specific, exceptional error conditions, deriving from
Exception.
defmodule Codexis.ConversionError do
defexception [:message, :source_file]
@impl true
def exception(opts) do
message = Keyword.get(opts, :message, "Conversion failed")
source_file = Keyword.get(opts, :source_file)
%__MODULE__{message: message, source_file: source_file}
end
end
- Follow error handling strategy from ADR-0006 and recovery strategies from ADR-0019.
Logging and Validation
- Use structured logging with
Loggerand metadata:
Logger.error("Conversion failed",
source: file_path,
error_code: :invalid_format,
suggestion: "Check LaTeX syntax"
)
- Use Ecto changesets for data validation before database operations.
- Handle validation errors gracefully in controllers and display appropriate user feedback (e.g., flash messages).
General Recovery
- Implement retries with exponential backoff for transient failures (e.g., using
Retrylibrary or custom logic). - Use circuit breakers for external dependencies prone to failure.
- Preserve state during failures where possible to allow for recovery or inspection.
UI and Styling
The selection of primary UI technologies and styling frameworks (e.g., Phoenix LiveView, Tailwind CSS) is documented in relevant ADRs. Key conventions include:
- Use Phoenix LiveView for dynamic, real-time user interactions.
- Implement responsive design using Tailwind CSS.
- Utilize Petal Components for pre-built UI features where appropriate.
- Use Phoenix view helpers and templates (
.heex) to keep views DRY.
Performance
- Use database indexing effectively based on query patterns.
- Implement caching strategies (e.g., ETS, ConCache, Redis) for frequently accessed or computationally expensive data.
- Use Ecto's
preloadfunction to avoid N+1 query problems. - Optimize database queries using
preload,join, orselectwhere necessary.
Security
- Implement proper authentication and authorization (e.g., using
phx.gen.auth, Guardian, Pow). - Use Ecto changesets and parameter casting (
cast/3,cast_assoc/3, etc.) to act as strong parameters in controllers. - Protect against common web vulnerabilities (XSS, CSRF, SQL injection) by following Phoenix security guides.
- Keep dependencies updated to patch security vulnerabilities.
Testing
- Use the ExUnit framework.
- Follow TDD practices (ADR-0012).
- Organize tests using
describeandtestblocks. - Use shared test helpers/modules in
test/support/(e.g.,RepoCase,ConnCase). - Use Mox for mocking external dependencies and behaviours.
- Use fixtures in
test/fixtures/for test data, potentially using libraries like ExMachina for generation. - Tag tests using
@tagfor categorization (e.g.,@tag :unit,@tag :integration). - Implement property-based testing with StreamData where appropriate for robustness.
- Do not prefix unused variables in tests with
_. This practice, established by an ADR, ensures test explicitness. If a variable is unused, remove it or utilize it in an assertion. - Do not skip or exclude tests (e.g., using
@tag :skiporexclude: true). As per an ADR decision, all tests in the suite must pass to maintain test suite integrity. Fix failing tests or remove them if they are no longer relevant.
Documentation
- Use ExDoc for generating documentation.
- Write module documentation (
@moduledoc) explaining:- Purpose of the module.
- Key concepts.
- Examples of usage.
- Write function documentation (
@doc) for all public functions, including:- Purpose.
- Parameters and types (
@spec). - Return values.
- Examples using
iex>.
- Document non-obvious decisions or complex logic in code comments.
- Keep ADR references in relevant modules.
- Update
digitization-workflow.mdfor any process changes.
Version Control
Commit Messages
- Use conventional commit types:
feat:New functionalityfix:Bug repairrefactor:Code restructuringdocs:Documentation changeschore:Maintenance tasks (build process, tooling)test:Adding missing tests or correcting existing testsstyle:Code style changes (formatting, etc.)
- Reference issues/JIRA tickets or
task-mastertask IDs in the commit body or footer. - Squash WIP commits before merging.
Example:
feat: Add EPUB validation step
- Implement EPUBCheck integration via Command Framework.
- Add error recovery for validation failures using Retry strategy.
- Update documentation with EPUBCheck requirements.
Refs: PROJ-123, task-7
Git Town Workflow
-
Use Git Town commands for branch management:
# Start new feature git town hack feature/my-feature # Sync branch with main git town sync # Create pull request git town new-pull-request # Ship completed feature git town ship -
Branch naming conventions:
feature/<name>: New functionalityfix/<name>: Bug fixesrefactor/<name>: Code improvementschore/<name>: Maintenance tasks
-
Configuration rules:
- Main branch:
main - Perennial branches:
main(adjust if using adevelopbranch) - Sync strategy: rebase
- Main branch:
Branching
- Use GitHub Flow with short-lived feature branches based off
main.
Workflow Practices
- Follow eXtreme Programming (XP) principles where applicable (ADR-0015).
- Implement pair programming for complex features or critical bug fixes.
- Use test-driven development (TDD) (ADR-0012).
- Conduct regular code reviews via Pull Requests.
- Utilize
task-masterfor managing development tasks:- Generate initial tasks from Product Requirements Documents (PRDs) using
task-master parse-prd. - Identify the next task to work on using
task-master next. - Update task status using
task-master set-statusupon completion. - Break down complex tasks using
task-master expand. - Keep tasks aligned with implementation changes using
task-master update.
- Generate initial tasks from Product Requirements Documents (PRDs) using
- Maintain a clean CI/CD pipeline with:
- Credo static analysis checks.
- Test suite execution (aim for high coverage, e.g., 85%+).
- Code formatting validation (
mix format --check-formatted). - Dialyzer checks for type consistency.
References
- Follow the official Phoenix Guides for best practices in routing, controllers, contexts, views, and other Phoenix components.
- Adhere to the community Elixir Style Guide (largely enforced by Credo and
mix format).