Coding Standards Document Demo 3 - COS301-SE-2026/Mealchemy GitHub Wiki
This document defines the coding conventions, architectural standards, and repository structure for Mealchemy. Compliance with these standards promotes consistency, clarity, maintainability, reliability, security, and efficiency across the codebase.
- Core Engineering Principles
- Repository Structure
- Best Practices and Guidelines
- Git and Pull Request Standards
- API and Data Contracts
- Frontend: Flutter and Dart
- Backend: Spring Boot and Java
- Recommendation Engine: Python
- Security and Authentication
- Offline and Local Storage
- External Services
- Nutrition and Measurement Data
- Maintenance
Avoid duplicating logic. Shared behaviour should be extracted into reusable components, services, utilities, repositories, or widgets where appropriate.
DRY supports maintainability by ensuring that a change generally needs to be made in one place.
Prefer the simplest implementation that correctly meets the requirement. Simple solutions are easier to understand, test, review, maintain, and extend.
Unnecessary abstractions and complexity should be avoided.
Mealchemy is maintained as a monorepo under the COS301-SE-2026 organisation.
The top-level repository structure is:
Mealchemy/
βββ .github/
β βββ workflows/ β CI/CD workflows
β βββ codecov.yml β Codecov configuration
β βββ labeler.yml β Pull request labelling
β βββ pull_request_template.md
βββ .vscode/
β βββ settings.json
βββ backend/
β βββ src/main/java/com/mealchemy/
β β βββ auth/
β β βββ category/
β β βββ config/
β β βββ cuisinetype/
β β βββ equipment/
β β βββ ingredient/
β β βββ nutritionalcalculator/
β β βββ pantry/
β β βββ preference/
β β βββ recipe/
β β βββ shared/
β β βββ shoppinglist/
β β βββ unit/
β β βββ vault/
β β βββ MealchemyApplication.java
β βββ src/main/resources/
β βββ src/test/ β Backend automated tests
β βββ pom.xml
β βββ mvnw
β βββ mvnw.cmd
βββ database/ β Flyway SQL migrations
βββ design-spec/ β Brand guide and wireframes
βββ diagrams/
βββ docs/
βββ e2e/ β Maestro end-to-end flows
βββ engine/ β Python recommendation engine
βββ frontend/
β βββ lib/
β β βββ core/
β β β βββ connectivity/
β β β βββ constants/
β β β βββ providers/
β β β βββ routes/
β β β βββ services/
β β β βββ shared_widgets/
β β β βββ theme/
β β β βββ utils/
β β βββ features/
β β β βββ auth/
β β β βββ dashboard/
β β β βββ discovery/
β β β βββ guided_discovery/
β β β βββ help/
β β β βββ ingredients/
β β β βββ offline/
β β β βββ pantry/
β β β βββ preference/
β β β βββ recipe/
β β β βββ shopping_lists/
β β β βββ vault/
β β βββ main.dart
β βββ test/
β βββ android/
β βββ ios/
β βββ macos/
β βββ pubspec.yaml
βββ infrastructure/ β Docker and Compose configuration
βββ landing/
βββ .env.example
βββ README.md
βββ wiki/
The repository tree must be updated when significant packages, services, or top-level directories are introduced or removed.
- Consistency: Follow the agreed naming conventions, architecture, module layouts, and style rules throughout the codebase.
- Clarity: Use names that describe the purpose of classes, variables, methods, files, and components.
- Separation of concerns: Presentation, business logic, state management, persistence, and external-service integration must remain appropriately separated.
- Code review: Code must be reviewed before it is merged into a protected shared branch.
- CI/CD: Required linting, testing, build, and coverage checks must pass before merging.
- Error handling: Provide clear, user-friendly error messages without exposing implementation details.
- Documentation: Update relevant technical and user-facing documentation when behaviour or contracts change.
- Testability: Dependencies should be injectable or replaceable so automated tests do not require live external systems.
- Scope: Pull requests should contain one coherent change and should avoid unrelated formatting or generated-file changes.
- Direct dependencies must be declared explicitly in the appropriate project configuration.
- Reproducible resolved versions should be retained through approved lockfiles where the ecosystem supports them.
- Dependency constraints and upgrades must be reviewed rather than changed incidentally.
- Generated lockfiles must not be edited manually.
- Unused dependencies should be removed.
- Security-related dependency updates should be prioritised and tested.
- A dependency should not be added when the required functionality can reasonably be achieved with an existing approved dependency or platform capability.
- Generated files must not be edited manually.
- Generated files must be recreated using the project-approved generation command after their source definitions change.
- Generated database bindings, platform registrants, and lockfiles should not be modified by unrelated work.
- Generated files should be committed only where required by the repositoryβs established policy.
- Developers must inspect generated changes before committing them to ensure that no unrelated output has been introduced.
- Branches use an agreed prefix such as
feature/,fix/,docs/,test/, orrefactor/. - Feature branches use the singular
feature/prefix rather thanfeatures/. - Branch names should identify the work clearly and may include the developerβs name where required by the team.
- An example feature branch is
feature/NutritionApiIntegration/SofiaFinlayson. - Commits should represent one coherent change.
- Commit messages should be concise, descriptive, and use the teamβs agreed conventional or imperative format.
- Feature branches should incorporate the latest
devbefore final review. - Merge conflicts must preserve both the featureβs intended changes and valid changes already merged into
dev. - Pull requests should explain:
- what changed;
- which tests were added or updated;
- which commands were run;
- whether manual testing was performed;
- whether API, database, migration, security, or offline behaviour is affected;
- any known limitations.
- No secrets, credentials, personal configuration, or environment files may be committed.
- No unrelated formatting, dependency, lockfile, generated-file, or platform changes should be included in a pull request.
- Required CI checks and peer review must be completed before merging.
- Backend JSON field names use
snake_case, for examplecategory_id,recipe_id,created_at, andinclude_available_pantry_items. - Java uses
lowerCamelCasecomponent and field names internally. - Java DTOs use explicit
@JsonPropertyannotations where the JSON name differs from the Java name. - Dart models use
lowerCamelCasefields internally and explicitly mapsnake_caseJSON fields infromJsonmethods and request bodies. - Frontend code must not assume that JSON integers and decimal values always have the same runtime numeric type.
- Numeric parsing should safely accept compatible
numvalues. - Optional and nullable API fields must be represented honestly.
- Non-null assertions must not be used unless the API or application contract guarantees that the value exists.
- Request and response DTOs are public API contracts and must not expose persistence entities directly.
- API repositories must validate and map response data before exposing it to providers or widgets.
- Contract changes must update:
- backend DTOs;
- controller and service tests;
- frontend request construction;
- frontend response mapping;
- repository tests;
- relevant technical documentation.
- Coordinated frontend and backend pull requests should clearly document temporary dependencies or rollout order.
- The frontend must not duplicate calculations or conversions that the backend contract defines as server responsibilities.
-
Classes, enums, extensions, and typedefs: Use
UpperCamelCase. -
Variables, parameters, methods, providers, and functions: Use
lowerCamelCase, for exampleloadPantryItems(). -
Files and directories: Use
lowercase_with_underscores, for exampleguided_discovery_provider.dart. -
Feature folders: Use
lowercase_with_underscores, for exampleshopping_lists. -
Feature subfolders: Use lowercase names such as
models/,providers/,repositories/,screens/, andwidgets/. - Private declarations: Begin with an underscore.
-
Boolean names: Describe a true condition, for example
isLoading,hasCachedData, andcanDeleteShoppingList. -
Acronyms: Follow normal Dart casing, for example
apiService,recipeId, andusdaSourceId. -
Test files: Mirror the source filename and end with
_test.dart. For example,recipe_provider.dartcorresponds torecipe_provider_test.dart. - Directory names are case-sensitive and must remain lowercase.
- Existing names such as
Molecules/should be migrated tomolecules/when it is safe to update every reference.
Flutter code uses a feature-first structure under lib/features/<feature>/.
- Models represent application data.
- Screens and widgets form the presentation layer.
- Riverpod providers and notifiers coordinate repositories and expose application state.
- Repositories provide access to API, mock, cached, or other data sources.
- Feature-specific components should remain inside their owning feature.
- Application-wide shared code belongs under
lib/core/. - Screens and widgets must not construct HTTP requests directly.
- Screens and widgets must not access local databases directly.
- Business and data-access logic should not be duplicated across widgets.
- Models should be immutable where practical.
- Prefer constructors, final fields, and
copyWithmethods where state transitions require modified copies.
- Define a repository interface when a feature supports API, mock, cached, or test implementations.
- API repositories are responsible for endpoint paths, HTTP methods, request bodies, response validation, and JSON-to-model mapping.
- Cached repositories should wrap or implement the relevant repository contract rather than introducing unrelated access patterns.
- Providers are responsible for loading state, mutation state, invalidation, refresh, and updating state after successful operations.
- Widgets must not instantiate production repositories directly.
- Repository dependencies must be injectable or overridable.
- Tests must use provider overrides or injected repository implementations.
- Mock repositories must not silently replace API repositories in production configuration.
- Successful mutations must update or invalidate all affected state so backend changes appear without restarting the application.
- Repository methods should expose domain models rather than raw Dio responses or unvalidated JSON maps.
Every user-facing asynchronous feature must consider:
- Loading state.
- Successful data state.
- Empty state.
- Validation failure.
- API or repository error.
- Retry behaviour where appropriate.
- Offline or cached state where applicable.
User-facing errors must be understandable and must not expose stack traces, raw exception messages, database details, secrets, or internal implementation information.
- Reuse colours, typography, spacing, components, and controls from the existing design system.
- Avoid hard-coded colours and duplicate text styles where theme definitions exist.
- Screens must remain usable on supported mobile screen sizes without overflow.
- Scrollable content must remain reachable when fixed bottom controls are present.
- Fixed controls must not permanently obscure page content.
- Interactive controls should provide meaningful labels, tooltips, or semantics where appropriate.
- Touch targets should remain large enough for practical mobile use.
- Disabled controls must clearly communicate unavailable actions, particularly while offline.
- User-facing wording should remain consistent across screens, tests, Help content, and API feedback.
- Important user-facing changes should be manually inspected on a supported device or emulator.
- Platform-specific functionality must be guarded appropriately and must not be called from unsupported platforms.
Each feature may contain:
lib/features/<feature>/
βββ models/
βββ providers/
βββ repositories/
βββ screens/
βββ widgets/
βββ utils/ β Only where feature-specific utilities are required
Not every feature is required to contain every subfolder. Empty structural folders should not be created unnecessarily.
- The project uses the configured rules in
analysis_options.yaml. -
flutter analyzemust pass without errors or warnings before a pull request is submitted. - Analyzer warnings should be corrected rather than suppressed unless there is a documented technical reason.
- Dart code should be formatted using the project-approved Dart formatter.
- Unused imports, fields, parameters, and dependencies must be removed.
- Non-null assertions should be avoided where proper null handling is possible.
- Use
flutter testfor automated model, repository, provider, widget, and screen tests. - Use
flutter test --coveragewhen generating LCOV coverage reports. - Tests should mirror the relevant feature structure under
frontend/test/. - Focused tests should be run during implementation.
- The complete affected feature suite must pass before a pull request is opened.
- Tests must not require a live backend unless they are explicitly defined as integration or end-to-end tests.
- API repository tests should use mocked Dio responses or interceptors.
- Riverpod tests should use
ProviderContainer, dependency overrides, or repository fakes. - Widget tests should cover important loading, success, empty, validation, error, retry, and offline states where relevant.
- User-facing bug fixes should include regression tests where practical.
- Required CI and configured coverage checks must pass before merging.
Common frontend commands include:
flutter pub get
flutter analyze
flutter test
flutter test --coverage
flutter test test/features/<feature>
flutter test test/features/<feature>/<test_file>.dart-
Classes, records, interfaces, and enums: Use
UpperCamelCase. -
Variables, parameters, fields, and methods: Use
lowerCamelCase, for examplegetPantryIngredientById(). -
Packages: Use lowercase names without underscores, for example
shoppinglistandcuisinetype. -
Constants: Use
UPPER_SNAKE_CASE. -
Incoming DTOs: Filenames end with
Request.java. -
Outgoing DTOs: Filenames end with
Response.java. -
Test files: Mirror the source class name and end with
Test.java. For example,PantryService.javacorresponds toPantryServiceTest.java. -
Integration tests: Mirror the relevant class name and end with
IntegrationTest.java, for examplePantryControllerIntegrationTest.java.
Feature packages under com.mealchemy separate controllers, DTOs, models, repositories, services, external adapters, and utilities where required.
Dependencies should generally flow:
Controller β Service β Repository
β External provider or adapter
A feature may use the following structure:
com/mealchemy/<feature>/
βββ controller/
βββ dto/
βββ external/
βββ model/
βββ repository/
βββ service/
βββ util/
Only folders required by the feature should be created.
Non-feature-specific shared code, such as common enums, configuration, exception handling, and shared security behaviour, belongs in config/ or shared/.
Database migrations live in the top-level database/ directory.
- Controllers handle HTTP concerns, request validation, authentication principals, response status codes, and delegation to services.
- Controllers must not contain persistence or domain business logic.
- Services contain business rules and transaction boundaries.
- Repositories contain database-access operations and queries.
- External-service access must use an approved provider, client, or adapter abstraction.
- JPA entities represent persistence and must not be returned directly as public API responses.
- Request and response DTOs should use Java records where immutability and framework compatibility allow.
- Constructor injection is required.
- Field injection must not be used.
- Validation failures and domain errors should be translated through the shared exception-handling mechanism.
- Transactions that modify an aggregate should either complete fully or roll back fully.
- Nullable values must be handled explicitly.
- Logging must provide useful diagnostic context without exposing secrets or personal data.
- Flyway migration filenames use
V<number>__description.sql. - Existing migrations applied to a shared environment must not be edited.
- Corrections to applied schemas must use a new migration.
- Migrations should be reviewed for compatibility and possible data loss.
- Destructive migrations require explicit review and a documented recovery or backup plan.
- Database constraints, indexes, defaults, and nullability should match application assumptions.
- Schema and DTO changes must be coordinated where they affect public API contracts.
- Local database-volume deletion is a development reset procedure and must not be treated as a normal migration step.
- Backend Java follows the configured Google Java Style rules.
- Style is enforced through the Maven Checkstyle plugin.
- Required builds must fail when configured style checks fail.
- Formatting changes unrelated to the feature should be avoided.
- JUnit 5 is used for backend automated testing.
- Mockito is used to isolate service logic where appropriate.
- Spring Boot Test, MockMvc, and Spring Security Test are used for relevant integration and controller testing.
- Repository and migration behaviour should be tested against PostgreSQL where integration coverage is required.
- Backend tests must not depend on uncontrolled personal database records.
- Required Maven, Checkstyle, JaCoCo, and CI checks must pass before merging.
- Coverage requirements are defined by the active Maven, JaCoCo, Codecov, and CI configuration.
- The document must not claim a coverage threshold that is not actively configured and enforced.
Common backend commands include:
./mvnw test
./mvnw verify
./mvnw checkstyle:checkThe recommendation-engine standards remain provisional until its linting, type-checking, testing, and CI configuration are finalised.
- Files, functions, and variables use
snake_case. - Classes use
UpperCamelCase. - Constants use
UPPER_SNAKE_CASE.
- Ruff should be used for linting and formatting where configured.
- Mypy should be used for static type checking where configured.
- Type hints should be added to public functions and important internal boundaries.
- pytest and pytest-cov should be used where configured.
- Recommendation logic should be tested using controlled, repeatable fixtures.
- Automated tests must not depend on uncontrolled production data.
- Coverage requirements are defined by the active pytest, Codecov, and CI configuration.
- Secrets and credentials must not be committed to source control.
- Authentication tokens must be stored only in the approved secure-storage implementation.
- Tokens, passwords, JWT secrets, database credentials, and external API keys must never be logged or committed.
- Secrets must be supplied through ignored environment files, deployment configuration, or approved CI secret storage.
- Persisted user identity and token validity must be treated as separate concerns so cached data can be handled safely after token expiry.
- Unreadable or invalid secure-storage values must be handled without crashing.
- Authentication interceptors must avoid infinite refresh or retry loops.
- A failed request must not clear user-scoped cached data unless the application has confirmed that a different user is signing in.
- Protected backend endpoints must enforce authentication and authorisation.
- User-facing errors must not expose tokens, credentials, internal stack traces, database details, or sensitive configuration.
- Personal and user-specific data must only be accessible to authorised users.
- Security-sensitive changes require relevant tests and explicit review.
- Offline application data is read-only unless a feature explicitly implements an approved and tested synchronisation strategy.
- Mutation requests must be blocked centrally while offline.
- The UI must disable or otherwise protect matching mutation controls while offline.
- Cached application data must be scoped by user identity.
- Signing in as a different user must switch cache namespaces.
- Cached data must never leak between users.
- Same-user reauthentication may preserve that userβs valid cache.
- Partial or failed synchronisation must not replace valid cached aggregates with incomplete data.
- Aggregates such as recipes and shopping lists with child records should be stored transactionally.
- Cache freshness must be tracked independently for each collection and scope.
- Database and image caches must use platform-appropriate cache storage.
- Cache files should be excluded from cloud backup where supported.
- Cached-data interfaces must clearly show offline status and freshness.
- SQLite schemas and generated database files must be updated through the project-approved generation and migration process.
- Tests must use temporary, isolated, or in-memory storage where practical.
- Tests must not read or overwrite a developerβs actual secure-storage credentials or user cache.
- Reconnection must restore online state and mutation controls when backend reachability is confirmed.
External services, including USDA, must be accessed through an adapter or provider abstraction rather than directly from controllers, screens, or widgets.
- External response models must be translated into internal Mealchemy models.
- Timeouts, missing fields, invalid responses, unavailable services, and rate limits must be handled explicitly.
- External API keys must be supplied through ignored environment files or CI secrets.
- Unit, provider, repository, and widget tests must mock external services and must not consume live API quotas.
- External data provenance should be retained where required, for example through
source_apiandsource_id. - External search results that require local catalogue information must complete the approved import and category-selection flow.
- User-facing nutritional information derived from external data must include the approved accuracy disclaimer.
- External-service failures must not crash the application or expose raw third-party error responses to users.
- Canonical quantity normalisation and unit conversion are backend responsibilities.
- Weight is stored canonically in grams and volume in millilitres where defined by the backend contract.
- The frontend displays values in the userβs preferred measurement system using values returned by the API.
- The frontend must not duplicate backend nutritional scaling or unit-conversion logic.
- General units that do not require conversion must remain consistent with the backend contract.
- Nutritional calculator values may be displayed per recipe or per serving using the corresponding API response fields.
- The frontend should render the six currently supported nutritional fields: calories, protein, carbohydrates, fat, fibre, and sodium.
- Nutritional values should be formatted consistently without altering their underlying model values.
- Ingredient calorie percentages may be derived from returned ingredient and recipe calorie totals.
- Percentage calculations must safely handle zero total calories.
- Nutritional values should be treated as estimates where they depend on external catalogue data or backend conversion assumptions.
- The Nutrition interface must display the approved USDA accuracy disclaimer.
This is a living document. It must be reviewed and updated as the Mealchemy architecture, supported platforms, development workflow, dependencies, tooling, and CI configuration change.
The document should be updated when:
- A new feature package or top-level service is introduced.
- Repository structure changes.
- API naming or data-contract standards change.
- A new external service is added.
- Offline, caching, authentication, or storage behaviour changes.
- New linting, formatting, testing, coverage, or CI requirements are introduced.
- Existing documented standards no longer match the implemented codebase.
- The team agrees on revised naming, branching, review, or deployment practices.
Changes to this document should be reviewed by the team so that the standards remain accurate, practical, and consistently applied.