Skip to content

πŸ’»Coding, In-Code Documentation, and Testing Guidelines ​

This page contains information on coding guidelines, in-code documentation and testing code (if the title wasn't clear enough yetπŸ˜‰).

🚨 Code guidelines ​

  • Front-end Tablet first approach than Phone and last Computer. Thus, the frontend must be mobile friendly.
    • To simplify this process for developers, use the
      • IPad pro 12.9-inch (1024 x 1366) as a reference for the tablet view.
      • Galaxy S20 ultra (412 x 915) as a reference for the mobile view.
    • Use responsively for testing the viewports.

      Accessibility

      Ensure that developers regularly toggle the visual impairment settings and check readability across all views. This is essential as our team includes people with visual impairments. All layouts and content should maintain clear readability, contrasting colors, and be screen-reader friendly to ensure inclusivity.

  • Jetbrains "code with me" will be used for pair programming.
  • Follow the following coding standards:
    Standards
    • SOLID
    • KISS
    • DRY

    Take a look at this video for a better understanding of these principles:

  • The code must be written in a way that it is easy to read and understand. This is done by using the correct naming conventions and by using comments where necessary.
  • The code must be written in English
  • Add to-dos to unfinished work:
    To-do
    javascript
    export default {
      //todo "Feature" - unfinised work - link to issue on github
    }
  • Follow the Java naming conventions.
  • API responses should follow the HATEOAS HAL spec.
  • For IDs, we don't use integer or long values but UUIDs.

πŸ“ In-code documentation ​

  • All interfaces must have a Javadocs block.
  • All overwritten or non-self explanatory functions within a class need to have a Javadocs block. Simple CRUD operations don't need this.
  • All non-self explanatory classes must have a Javadocs block. Simple DTOs or repositories don't need this.
  • Include a description of the function / class.
  • Include a description of the parameters and return types (if applicable) and link to other classes.
Example
java
/**
 * Set a user as an owner of a farmer
 * @param farmerId The id of the farmer
 * @see Farmer                
 * @param userId The id of the user to set as owner
 * @return ResponseEntity with a message indicating success
 */

πŸ“ Logging ​

🎨 Front end logging ​

This document outlines the standard for implementing logging in the frontend application. Following these guidelines ensures that our logs are consistent, structured, and useful for debugging, while remaining disabled in production for performance.

Core Principles

  • Do Not Use console.log: Refrain from using console.log, console.warn, etc., directly in the code. While acceptable for temporary, local debugging, these calls must be removed before committing.
  • Use the Centralized LoggerService: All logging must be performed through the centralized LoggerService. This service provides scoped loggers, structured formatting, and runtime-level filtering.
  • Keep Logic Out of Components: Logging should only be used in services, Pinia stores, composables, and middleware. If you feel the need to add complex logging within a Vue component, it is a sign that the logic is too complex and should be refactored into a composable or a store.

Implementation Guide ​

Creating a Logger Instance ​

To use the logger, import loggerFactory and create a scoped instance. The best practice for classes (like services or API endpoints) is to create the logger as a private, readonly property in the constructor. The scope name should be unique and descriptive for that module.

Example for a service:

javascript
import { ILogger, loggerFactory } from '@/services/logger.service';

class MyExampleService {
    private readonly log: ILogger;

    constructor() {
        this.log = loggerFactory.scoped('MyExampleService');
    }

    public doSomething(userId: string): void {
        this.log.info('doSomething', 'Starting operation for user', { userId });
        // ... function logic ...
    }
}
Writing Log Messages (Structured Logging) ​

Always use the structured format: log.level('functionName', 'message', { contextObject }).

  • functionName (string): The name of the method where the log originates.
  • message (string): A clear, concise description of the event.
  • contextObject (object): An optional object containing relevant variables or data. This is crucial for effective debugging.
Measuring Performance ​

Use the built-in timers to measure the duration of operations. These timers are only active when the log level is set to INFO.

javascript
async function myLongOperation() {
    this.log.time('myLongOperation', 'Starting complex calculation');

    // ... long-running code ...
    await somePromise;

    this.log.timeEnd('myLongOperation', 'Finished complex calculation');
}
Controlling Log Output (Developer Workflow) ​

Logging is controlled at runtime via the in-browser Logger Configuration Panel. Do not modify the code to enable or disable logs.

  • Access the Panel: In the development environment, click the floating gear icon (βš™οΈ) on the screen.
  • Configure: Use the panel to:
    • Set a global Default Level.
    • Create Groups to organize your modules.
    • Set specific log levels for individual Modules or entire Groups.
    • Import/Export your configuration to share with other developers.
  • Apply: Click "Apply & Reload" to activate your new settings.

This configuration can also be set manually in the browser console via the log_levels and log_groups items in localStorage.

πŸ’» Back end logging ​

  • Use the @Slf4j annotation from lombok.extern.slf4j.Slf4j; for logging.
  • Never log the full stack trace, but either use a custom message or log the exception message.
  • Never use the Sout (System.out.println) for logging.
  • Use the appropriate log level:
    • log.debug for detailed information, typically of interest only when diagnosing problems during development.
    • log.info for informational messages that highlight the progress of the application at a coarse-grained level (like logging a create or delete function).
    • log.warn for potentially harmful situations.
    • log.error for error events that might still allow the application to continue running, often used inside catch blocks or together with throwing exceptions.
    • log.trace for more fine-grained informational events than the debug. This log level is typically used to capture detailed information about the flow through the system, such as entering and exiting complex methods or functions. (we barely use it).

Log levels

Adjust the log level of the application to view the desired amount of log messages. This can be done in the application.yml file. For all developer written logs you can adjust: logging.level.flowcontrol.'modulename'.* : debug/info/warn/error/trace For the production environment, the log level is set inside the docker compose file. Adjusting these is not desired!

πŸ§ͺ Testing ​

🎨 Front end testing ​

FE testing

Due to the responsive nature of our Vue FE application, it is crucial to conduct various tests across different screen sizes, different users, databases, etc. This does mean that automated testing is not always possible. To cover this we use a FE testing checklist that we go over before each release. The following guidelines are to assure this checklist is as complete as possible.

  • On creating new FE functionality, contact Maarten or Rik about the new features so that they can be added in the testing checklist.
  • When adjusting existing FE functionality, check if the testing checklist needs to be updated. If so, contact Maarten or Rik.
  • When performing the testing, go over the checklist using different screen sizes (mobile, tablet, desktop).
  • When performing the testing, go over the checklist using different user roles (admin, farmer, planner, production).

πŸ’» Back end testing ​

BE testing

We are still investigating the best way to fully cover the BE functionality with automated tests, without having to spend months writing and maintaining these tests. This means that currently we only have strict guidelines for writing the API tests. These tests are meant to check the RBAC configuration and the validation of the request objects.

  • All new API endpoints must have API tests.
  • All seperate modules must have a EndpointCoverageVerificationTest.java file that checks if all endpoints have API tests.
  • Each endpoint gets tested with a positive flow purely checking the RBAC configuration.
  • Besides the RBAC test, each endpoint must have negative tests checking the validation of the request objects.
  • Use the existing API tests as a reference for writing new API tests.

πŸ“š Performing tests ​

  • API tests can be performed manually using the Maven command:
bash
mvn test

This can either be done on the complete repository or on a specific module.

  • Api tests are also performed automatically in the CI/CD pipeline. See the pipeline documentation for more information.