- Feis Code Styleguide
- 1. Introduction
- 1.1. Guide Notes
- 2. Code Formatting
- 3. Codebase Structure
- 4. Version Control
- 5. Document Changelog
- 6. Credits
This document serves as the complete definition of all the fashion rules to follow when working with projects developed by mfacecchia.
Considering that this document defines my personal styling preferences when writing code, the rules specified later on does not represent the only way of actually writing code, and therefore any other styling preference can be considered valid. The rules in this document apply for any programming language (even though there might be few slight differences between some, which will be specified on the appropriate section of this document). Anyways, for a more detailed breakdown of all styling rules per programming language, it's advised to refer to the related programming language's documentations. Some links to the official programming languages' style guides I generally work with can be easily consulted by following the below hyperlinks.
The naming for source files, variables, constants, methods and functions, and any other programming language-related concept should follow the rules defined by the programming language, therefore, it's strongly advised to adhere to such rules to keep the source code consistent with most of the other programs you will work with.
All source files should be encoded using UTF-8, and all lines should
terminate with a \n character (ASCII code 0x10).
On most of my projects, there will probably be an .editorconfig file or similar
configuration file to easily follow some of the basic rules on spacing,
variables casing, and more.
Delimiting if, else, elif, for, do, while statements, and
function definitions with curly braces is strongly advised, even if its body
contains only one statement. Moreover, to keep the code as readable as possible,
it's advised to place the actual body on a new line if it contains only one statement.
About else, and elseif (or elif depending on your preferences), it's fine to
place them on the same line of the if statement closing bracket.
Opening brackets MUST be placed on the same the statement declaration line.
This applies to all other programming-language specific statements that expect
curly braces as styling preference.
This does not apply to Python statements since, in such specific case,
it's expected to use colons (:) instead, and the same applies to all other
programming languages which expect similar styling rule.
Examples:
// Correct
fn sum(n1: i32, n2: i32) -> i32 {
n1 + n2
}
// Correct
fn even_numbers_sum(vec: &Vec<i32>) -> i32 {
let mut result: i32 = 0;
// More lines, but more readable
for n in vec.iter() {
if (*n % 2 == 0) {
result += *n;
}
}
// Syntactically correct, but less readable
for n in vec.iter() {
if (*n % 2 == 0) { result += *n; }
}
// Again, syntactically correct, but not-so-much readable
for n in vec.iter()
{
if (*n % 2 == 0)
{
result += *n;
}
}
result
}An empty code block can either be closed immediately on the same line, or closed on a new line.
Examples:
// Acceptable
fn empty_function() -> () {}
// Equally acceptable
fn empty_function() -> () {
}Indentation should follow programming language-specific directives, to ensure consistency
between all other codebases. For example, Java source files should use four spaces,
TypeScript source files should have two spaces, etc...
It's advised to use the space character (ASCII code 0x20) rather than
the horizontal tab sign (ASCII code 0x09) to ensure consistency on
all kinds of text editors.
Indentation rules also include continuation lines indent rules(which
may differ per language).
The use of grouping parentheses is advised to make the code more readable,
and helps keeping the general code syntax more consistent.
This rule does not apply for situations in which it's syntactically impossible
to use grouping parentheses by programming language design (such as in a Python
for loop).
A smart usage of spacing can significantly improve code quality and readability. The following rules of thumb help making the code more consistent and easily understandable by all developers working on the project. If the programming language you're working with enforces some specific spacing rules, then it's advised to follow those rules.
- Use one blank line to separate methods and functions, and to logically separate the sections within a scope.
- Use one blank line to separate attributes declarations and methods within a class.
- Use one blank line to logically separate imports from different packages.
- Even though this is not mandatory, it's advised to place a single blank line at the end of each source file
- No trailing spaces
Examples:
// Correct usage of spacing rules
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import com.feis.sampleproject.features.dataLoader.repository.DataRepository;
public class DataLoader {
private DataRepository repository;
private static final Logger logger = LogManager.getLogger(DataLoader.class);
public DataLoader(DataRepository repository) {
this.repository = repository;
}
private void loadData() {
String authId = SecurityUtils.getAuthenticatedUser().getId();
List<Data> data = repository.findByUserId(authId);
return data;
}
}// Incorrect, difficult to read
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import com.feis.sampleproject.features.dataLoader.repository.DataRepository;
public class DataLoader {
private DataRepository repository;
private static final Logger logger = LogManager.getLogger(DataLoader.class);
public DataLoader(DataRepository repository) {
this.repository = repository;
}
private void loadData() {
String authId = SecurityUtils.getAuthenticatedUser().getId();
List<Data> data = repository.findByUserId(authId);
return data;
}
}Always include one space (ASCII code 0x20) before and after all
assignment operators, comparison operators, and arithmetic operators.
Examples:
// Correct usage of spacing rules
public User findUserById(int id) {
Optional<User> optionalUser = userRepository.findById(id);
if (optionalUser.isEmpty()) {
throw new NotFoundException("User with ID " + id + " not found.");
}
return optionalUser.get();
}// Incorrect, not readable enough
public User findUserById(int id){
Optional<User> optionalUser=userRepository.findById(id);
if (optionalUser.isEmpty()) {
// Uneven spacing on the "plus" operator, incorrect
throw new NotFoundException("User with ID " +id+ " not found.");
}
return optionalUser.get();
}There should be no blank space between neither annotation definition and its subject, and other annotations for the same. If multiple attributes contain an annotation, then it's advised to place a blank line to keep the declarations clean and understandable.
Examples:
// Correct
@AllArgsConstructor
@Service
public UserService {
// Incorrect
@Autowired
private UserRepository repository;
}public UserService {
// Clean and clear separation of declarations
@Autowired
private UserRepository repository;
@Autowired
private UserGroupsService userGroupsService;
}public UserService {
// Incorrect, in case of tons of attributes this can
// become quite difficult to read
@Autowired
private UserRepository repository;
@Autowired
private UserGroupsService userGroupsService;
}There should be even one-character spacing for either opening and closing brackets. An exception is made in methods and functions declaration, which brackets will contain a single space only after the closing, but not before the opening one. This rule does not apply on methods/functions calling since, in that specific case, it's required to keep the arguments definition without any spacing before and after the opening and closing brackets by most programming language design.
Examples:
// Correct usage of spacing rules
public User findUserById(int id) {
Optional<User> optionalUser = userRepository.findById(id);
// Correct, even spacing between brackets opening and closing
if (optionalUser.isEmpty()) {
throw new NotFoundException("User with ID " + id + " not found.");
}
return optionalUser.get();
}// Incorrect, no spacing on the closing line
public User findUserById(int id){
Optional<User> optionalUser = userRepository.findById(id);
// No spacing between either the `if` statement, and the opening curly bracket
if(optionalUser.isEmpty()){
throw new NotFoundException("User with ID " + id + " not found.");
}
return optionalUser.get();
}Comments should always begin with a space. This rule applies to all types of comment declarations (either single, and multi line).
Examples:
// Correct
public void doSomething() {
// ... Some logic here ...
}
//Incorrect, needs a space
public void doSomething() {
// ... Some logic here ...
}Every variable declaration should declares only one variable. Declaring multiple variables in a single line is discouraged since it can become misleading or harder to read and understand in larger codebases.
Examples:
// Incorrect, less readable
int a = 5, b = 10;
// Cleaner, better solution
int a = 5;
int b = 10;Logging is very important and should be used everywhere possible to make the code as a whole more professional, and drastically improve debugging experience. Using console logs is strongly discouraged, and is more appropriate to make use logging libraries such as Log4J or any other one of your choosing. Logging should follow the following pattern to ensure consistency between all the other projects, and should include:
- Timestamp
- Class name (working with an OOP Language)
- Method name
- Brief description of the operation being executed
Examples:
public void findById(int id) {
try {
// Correct usage of logging level
logger.info("UserService.findById ::: Fetching user information for ID {}", id);
userRepository.findById(id).orElseThrow(
() -> new NotFoundException("User with ID {} not found");
);
} catch (NotFoundException e) {
// Incorrect, missing class name and method name,
// but correct usage of logging level
logger.error("User with ID {} was not found.", e);
throw e;
} catch (Exception e) {
// Correct logging pattern and level
logger.error("UserService.findById ::: Unexpected error while fetching user info.", e);
throw e;
}
}The use of the appropriate logging levels helps the developer understanding the
message severity, providing clear runtime insights about the application status,
and enabling effective per-level logs filtering.
The most used log levels in my projects are mainly INFO for messages about
a method execution, and ERROR in catch blocks (or except in certain
programming languages) to log the exception stack trace, but using
the other levels in some specific cases is accepted as well.
Using comments now and then can help other developers (and yourself) better understand the purpose of a specific code block, and therefore its usage is advised. It's also advised, though, not to abuse comments, rather prefer using them for particularly tricky code blocks, or to explain the reason behind the choice of a solution over another. Indent the comments the same level as the surrounding code so that they can be more easily read, and prefer multi line comment blocks over single line ones.
While working on a feature, it's quite common to leave some comments as a note for a later time. To differentiate the scopes and quickly understand the "problem", it's advised to prepend specific tags at the beginning of the comment; some of those tags include:
- TODO - To mark something that's supposed to be added later on during the development
- FIXME - To report a bug that's to be fixed
- WIP - To mark a code snippet as something that's intended to be a preview and, therefore is not intended to be added to version control's main branch or not ready for production yet.
To more easily distinguish the different tags, you may want to install an extension for your favourite IDE to highlight the comments based on such tags.
Examples:
public User getUserById(int id) throws NoSuchElementException {
// TODO: Add a `try/catch` to handle possible empty `Optional`
return repository.findById(id).get();
}
public User getUserByEmail(String email) throws NoSuchElementException {
// FIXME: Finding user by first name instead of Email
return repository.findByFirstName(email).get();
}
// WIP: still thinking about implementation
public User getUserByEmail(String email) {
// What to do here?
}Most programming languages offer inline documentation features through specific formatting rules. Using them to improve APIs understanding is advised (but not mandatory). Each programming language has its own formatting rules, so assure to carefully follow such rules while writing documentation.
Examples:
/**
* Validates the authentication token based on various
* checks such as expiry date, signature, and more.
*
* @param authToken the token to validate
*
* @return whether the authentication token is valid or not
* in form of boolean value
*/
public boolean validateAuthToken(Jwt authToken) {
// ... Some logic here ...
}/// Validates the authentication token based on various
/// checks such as expiry date, signature, and more.
///
/// # Arguments
/// * `auth_token` - The `Jwt` to validate.
///
/// # Returns
/// A `bool` value representing whether the authentication token
/// is valid or not
fn validate_auth_token(auth_token: &Jwt) -> bool {
// ... Some logic here ...
}This style guide section focuses on the packages organization of more complex projects, rather than simpler ones, therefore, this is no applicable for all projects.
Packages in my projects follow the feature-based organization, and are structured similarly this way:
- A
commonspackage, enclosing all types of classes and functions used by any package in the application which cannot be classified for a specific feature; this package generally contains utility functions and classes, configuration properties, custom exceptions, and more based on the software - A
featurespackage, enclosing any feature class and function, each one in a more specific package.
Optionally, the application can also contain a special package defined as security
enclosing security-related classes (if the application involves authentication).
Example (taken from a simplified version of my personal Maven Archetype):
src/
├── MainApplication.java
├── common/
│ ├── data/
│ │ ├── dto/
│ │ │ └── BaseGetDto.java
│ │ └── entity/
│ │ ├── BaseAuditingEntity.java
│ │ └── BaseEntity.java
│ ├── enums/
│ │ └── ValidationMessage.java
│ ├── exception/
│ │ ├── AuthenticationException.java
│ │ ├── ResourceNotFoundException.java
│ │ ├── ValidationException.java
│ │ ├── handler/
│ │ │ └── GeneralExceptionHandler.java
│ │ └── model/
│ │ └── ErrorResponse.java
│ ├── mapper/
│ │ └── BaseMapper.java
│ ├── repository/
│ │ └── BaseRepository.java
│ └── service/
│ ├── AbstractFileService.java
│ └── AbstractService.java
├── configuration/
│ └── CorsConfig.java
├── features/
│ └── user/
│ ├── data/
│ │ ├── User.java
│ │ └── dto/
│ │ ├── request/
│ │ │ ├── UserCreateDto.java
│ │ │ └── UserUpdateDto.java
│ │ └── response/
│ │ ├── UserDto.java
│ │ └── UserPageDto.java
│ ├── mapper/
│ │ └── UserMapper.java
│ ├── repository/
│ │ └── UserRepository.java
│ └── service/
│ └── UserService.java
└── security/
├── SecurityConfig.java
└── annotations/
├── HasRole.java
└── HasRoleAspect.java
This section explains the ideal structure a codebase should have, but it's important to state that this may or may not be ideal for every project as they can serve different scopes and, therefore, require a different packages organization.
Repositories should use kebab-case.
Branches should keep a consistent naming even though the naming such convention may vary based on the project. The casing to use is camelCase, and MUST NOT include spaces.
Examples:
#This is generally used for larger and more complex projects,
#usually involving sprint-based tasks organization
issue_{version}/task_{card code}
#These are acceptable formats for smaller projects as well
feat/authentication
fix/userDeletionCommits styling follow Conventional Commits rules. You can refer to their accurate documentation to know more about how to deliver the best quality in commits as well.
- 2025-10-31, 22:02 UTC - mfacecchia - Created Document
- 2025-11-01, 00:23 UTC - mfacecchia - Updated code formatting, and logging sections
- 2025-11-01, 12:32 UTC - mfacecchia - Updated spacing && version control rules
- 2025-11-01, 16:53 UTC - mfacecchia - Updated comments-related style rules
- 2025-11-01, 18:40 UTC - mfacecchia - Updated packages structure style rules
This document was written and curated by mfacecchia.