Software Structure

Portable Software Structure

It appears that you are using AdBlocking software. The cost of running this website is covered by advertisements. If you like it please feel free to a small amount of money to secure the future of this website.

This document illustrates a portable directory structure for software projects to promote reusability and simplify the development cycle.

The software development and deployment cycle usually involves numerous and tedious steps other than just "coding". Some steps can be automated by Integration or Continuous Delivery systems (CI/CD), others needs to be performed frequently on the development machine before committing the code. Common development steps often includes: download dependencies, run various static analysis tools to check if the project meets the minimum predefined set of quality metrics, execute unit tests and check code coverage, execute functional and integration tests, check or generate documentation, etc.

It is important to stress that even if there are intrinsic advantages of the proposed structure over other possible ones, the main advantages comes from the fact that we can reuse the same model multiple times in different projects. This means that we can simplify the software development and deployment cycle by largely reusing the same configuration, test, analysis, build and packaging scripts and procedures. As a result of this the proposed model is not static and it can be altered on a project-basis the minimum required to meet specific language or application needs.

The Model

Starting from the project root folder, the project is organized as follows:

  • README.md
    (required) Every project should start with a README.md file, a document in Markdown format containing the essential project information (i.e.: name, description, category, authors, copyright, license, links, contacts, ...) and some information on how to start and contribute. The README.md file is usually rendered by the online Versioning Control System (VCS) as an HTML page on the project landing page, so it is effectively the entry point for anyone approaching the project. For this reason it is important to fill it with the right essential and quick-start information. Please see the projects in the Example section for a practical reference.
  • LICENSE
    (required) This file contains the license terms in plain text. It is usually referenced in the README.md and embedded in the software package (e.g. RPM, DEB, or Docker image).
  • VERSION
    (required) This is a plain text file containing the semantic version of the software as a Single Source Of Truth (SSOT).
    This file is usually parsed by build scripts and embedded in the software package name and content.
    For more information please read: Software Versioning.
  • RELEASE
    (required) This is a plain text file containing the release or build number. It can be dynamically generated and overwritten during the packaging phase but it is better to keep it as a SSOT to test build and packaging scripts.
    This file is usually parsed by build scripts and embedded in the software package name and content.
    For more information please read: Software Versioning.
  • .gitignore
    (required) Assuming that we are using GIT as VCS, this file specifies intentionally untracked files that Git should ignore.
    This should contain at least the dynamically generated directory "target" that we use to store all the logs and artifacts resulting from the test, build and report commands.
  • src
    (required) This directory contains all and only the project source code. It may also contain sub-directories to logically organise the code in packages or namespaces. For some languages like GO it also contains the unit tests files. This directory should not contain any static resource like text files, configuration files or HTML pages.
  • test
    (optional) For languages that do not have a mechanism like golang to distinguish source files from test files, this directory contains the unit test files for the source code defined in "src". Usually the test files have the same name suffix or prefix of the correspondent source files and are located in the same relative position.
    This directory can eventually contains other type of tests organized in sub-folders, if these do not conflict with the unit testing system.
  • vendor
    (optional) This directory can be used to include specific versions of external projects that are dependencies for the main project.
    Usually these dependencies are managed using a specific vendoring tool but in some languages they can also be filled manually by paying attention on using the correct sub-directory paths.
  • resources
    (optional) This directory contains all the project static resources and packaging configurations.
    The resources that will be packaged will be added with their relative final destination path, following the Filesystem Hierarchy Standard (FHS) to avoid breaking the SELinux rules.
    For example, for a Linux service software we can have the following sub-folders:
    • debian : contains debian-specific packaging files;
    • rpm : contains RPM spec files;
    • git/hooks : contains git hook scripts that can be installed locally by developers;
    • test : contains resource files for testing;
    • DockerDeploy : contains the Dockerfile used to package the software as runnable Docker image;
    • etc/PROJECT : contains the project configuration files (usually JSON files);
    • etc/init.d/PROJECT : project init file, if required;
    • usr/share/man/man1/PROJECT.1 : manual file, if required;
    • usr/share/VENDOR-PROJECT : contains project static resources like HTML pages or templates.
  • database
    (optional) If the project has a database back-end this directory contains the latest full database schema (i.e. schema.sql) and the initial data (i.e. data.sql) used to populate the empty database.
    The database changes between each version of the software should be added as SQL files inside a sub-directory named "patch". Each patch file should contain the reference versions (from-to). For example: database/patch/schema_1.9.5-2.0.0.sql
  • example
    (optional) If the project is a library this directory contains application examples.
    Examples are particular important in Open Source projects but are also relevant for private projects as they help cutting the training and support time required for newcomers. Usually are easier and faster to understand than reading the full documentation.
  • doc
    (optional) For non trivial projects this directory contains all the extra documentation.
    The documentation should be organised hierarchically in Markdown or HTML format. It may also contains reference documents in PDF format.
    If the documentation is too large, too generic or subject to frequent changes, it is better to make it available on the project Website and add a link here or in the README.md file.
  • target
    (dynamic) This directory is intended to be automatically created and destroyed by various test, build and packaging scripts to store all the resulting logs, reports and artifacts.
    The reason to name such directory is because:
    1. we can easily exclude all artifacts from the VCS just by excluding this directory;
    2. the CI/CD systems usually do not allow to write anything outside the project (or pipeline) root;
    3. we can easily save or copy all the artifacts from the CI/CD system, Virtual Machine or Building Container just copying this directory without assuming anything about its content.
  • other global project files
    (optional) The project root directory should contain global project files, for example:
    • specific CI/CD configuration files (e.g.: ".travis.yml");
    • configuration files for the dependency tool (e.g. "composer.json");
    • configuration files for the unit testing tool (e.g. "phpunit.xml.dist");
    • other global configuration files;
    • a Makefile that contains standardized targets to execute common tasks like testing, source code static analysis, building, packaging, etc; so we can reuse and reproduce the same commands in multiple places (i.e. dev machines, CI/CD systems, testing systems).

Examples

 

© 1998-2023 – Nicola Asuni - Tecnick.com - All rights reserved.
about - disclaimer - privacy