Majd Yafi

Back-end Developer

Front-end Developer

Solution Architect

0

No products in the cart.

Majd Yafi
Majd Yafi
Majd Yafi
Majd Yafi

Back-end Developer

Front-end Developer

Solution Architect

Blog Post

Software Engineering Principles, Methods, and Methodologies

February 20, 2022 Software Engineering
Software Engineering Principles, Methods, and Methodologies

Introduction

This software engineering guidance is written to help Software Engineers, Developers and Solution Architects have a common understanding and unified view of the set of tools available to implement both functional and non-functional requirements.
Therefore, the aim of this guidance is to:

  • promote a consistent view of the approach taken to understand requirements and move them across the pipeline.
  • specify the scope of practical use of these tools.
  • provide a foundation for standards that projects must meet, policies and principles that engineers and teams (must/should/could) adopt.

System Requirements

Functional and non-functional requirements

Functional requirements should be delivered by the project’s business analyst and effort estimated by the team during Agile ceremonies. However, meeting the non-functional requirements is maybe more challenging. If the non-functional requirement is not quantifiable then it should be classified into the relevant category and monitored by the technical lead and approved by a specialist. Categories examples:
Performance requirements, maintainability requirements, safety requirements, reliability requirements, security requirements, and interoperability requirements.

Process Models

The process model is a step concerning the activities that are initiated by a trigger and continued to evolve through the development life-cycle. Through this step, the team should be able to identify the following:

  • system actors: get to know your team and identify the key actors for Agile ceremonies. For example 3-amigos.
  • identity software configuration requirements: these configurations are embodied into the system as environment variables but are never exposed in the code directly. Please take the opportunity to identify these settings and follow best practices when creating them either in the code when using infra-as-a-code or as environment variables in the cloud.
  • regulations: be aware of the regulations which might be restrictive.

Requirements Reusability and Elicitation

Some requirements are shared across multiple projects which means that multiple teams could be reinventing the wheel without being aware. Scaffolders could enhance this recurrent process by initiating the project with components that can hold the following information:

  • Goals or Business Goals: The skeleton app could define the expected output of the project without implementing the code.
  • Scaffolded projects can capture and reuse the domain knowledge gathered over experience. Therefore, it limits major mistakes that were learned in the past.
  • Business rules: scaffolders could enhance requirements gathering by implementing the skeleton apps to elicit core business rules.
  • Scenarios: some scenarios are promoted through the business. It’s handy to have those pre-created in the project for the developers to utilise.

Reqreuiemets and Scaffolders

Scaffolders can enable the following:

  • early detection of requirements conflicts and resolving them in the skeleton application.
  • find the boundaries between the current project and other projects in the same organisation.
  • implementing the overall architecture that drives the organisational goals consistently.
  • provide tools and means to capture information and visualise it (this page is an example of a scaffolded guidance document).

In principle, scaffolders can contribute to the following areas of software design:

 

Software Engineering Body of Knowledge

 

Component-based Design

The aim of the component-based design is to promote decoupling. Agile is largely based on component-based design. Component-based design is a set of practices and recommendations to help teams optimise the process of software development life-cycle. This includes the early stages of requirements collecting, converting requirements into manageable pieces of work and then providing a mechanism to implement and deliver each piece without introducing retrospective issues.

Agile captured the concept of component-based design and labelled each step. This documentation assumes that all teams are familiar with Agile. Scaffolder can construct projects that can do the following, out of the box:

Abstract away parts that change

In addition to SOLID principles, the component-based design enables teams to collaborate and modularise implementation. Modularisation is strongly encouraged. This cannot be achieved without abstraction. Abstraction, technically, can either be achieved using interfaces in object-oriented programming, or appropriate structuring of folders/files/functions in script languages. Scaffolders can create the structure in readiness for development using a standard and consistent approach.

Understand Continuous Integration and Continuous Deployment Concepts

Every developer in the team should familiarise themselves with CI/CD. It is everyone’s responsibility to progress their implementation from local dev environments to production going through UAT. Developers should have a good understanding of the pipeline steps and how to maintain it when it breaks.

Promote collaboration and Peer Reviews, improving the code quality.

Component-based design (Agile) enables collaborations by modularisation. However, teams should communicate and agree on common acceptance criteria. Whilst 3-amigos can achieve that ahead of development, peer reviews confirm the output of 3-amigos using code.

Increase Agility

Scaffolders will create the tools that you need to run Agile projects. However, it is the team’s responsibility to maintain their tickets and keep them all up to date. The tools will enable traceability and data persistence for all details as you move in the software development life-cycle.

Improve Portability (31) and also consider script languages.

Keep the implementation of modules within the same location and configure dependency injection to use the relevant implementation. Modularisation enables implementation replacement with zero cost. This may not always be possible if you’re using a scripting language. Scaffolders, however, will initiate the application with the tooling you need to achieve this.

Share common artefacts

Carefully constructs the shared components. Agree with every team that is involved in building these components on the expected input and output. Test them using the black-box approach and store them in a folder labelled shared component or use the convention of your project. Consult the tech lead, if not sure.

Automate mundane tasks.

Create a backlog of such tasks and seek to automate them during the sprint. Initially, it might consume time to automate them. However, this will reduce the overhead in the long run. Scaffolders, will automate the common ones and make them ready for you to use. Read the documentation carefully and ask the team lead to clarify which ones should/shouldn’t be used/implemented.

Enhances productivity / by optimising the process and aligning the team to work more efficiently.

Component-based design is designed to optimise the process. However, its elasticity enables teams to stretch the methodology to suit their needs. Therefore, discuss the best use of the methodology with your team and use the pre-installed template as a starting point for running the methodology.

Reusable code that is easy to main, lowers costs

Modules should be portable across devices and environments. Scaffolders will create the relevant settings for you and set standards for using environment variables, pipelines, git practices and other coding principles to enable lift and shift. Keep the consistency.

Architecture Design Decisions

Where possible run tests in parallel on the CICD build process to ensure faster execution and reduce queue time

Building the CICD pipeline is essential to all projects. I fail to see any exception to this. However, discuss it with the tech lead. it’s important that every PR is tested before it’s merged into the main branch. This will increase resilience. However, it is not always quick. Pipelines commonly run the following tasks:

  • Unit tests check.
  • Integration tests check.
  • UI tests check.
  • Accessibility check.
  • Code build.
  • Code package.
  • Artefact generation.
  • Certificates assignment.
  • security checks.
  • Dependencies (packages, containers) vulnerabilities checks.

Each step can consume some time and therefore, it’s the architecture design that will optimise this process.

Low Code, No Code Platforms

Even when you have access to low-code and no-code platforms code, you should not extend what is in there. Ideally, these platforms should be tested using the black-box model.
For Low code platforms such as Power Platform, unit testing is of little use, favour UI and Acceptance tests

Backend test stubs

Favour integration tests over UI / Acceptance tests where applicable, they are faster and easier to maintain. These can be implemented in the backend. Mocking objects is quick and safe. 15
Don’t rely on UI tests to create data, seed your own data.

Code quality

Treat test code as you would with production code, it is not a second-hand citizen. Check with your tech lead about the quality aim and coverage for tests. Tests should do the job of testing implementation. They should not be done to claim the project as TDD.

pipeline behaviour

Ensure tests are run as part of a PR and are run for every single commit. This can be easily achieved either in GitHub triggers or Azure DevOps. If the pipeline does not behave in such a way, please contact your tech lead to warning them.

Containers name matches and trusted sources

Scan Container images and only use approved images from a trusted registry. Be aware that malicious software uses containers or packages with similar names to the originals. Therefore, you should always check with the appropriate tools these dependencies.

GDPR and Sensitive data

Do not leave any sensitive information in log files e.g. connection strings or service names. Also, be aware of restrictions to storing user names, date of birth and other data that is maybe deemed sensitive. Consult the tech lead and business analyst in this regard to ensure that logs are kept sanitised.

Secure data

Use environment variables to inject sensitive credentials, and keep them out of source control. Only store the dev version of environment variables and make sure it is listed in the gitignore file.

Follow best practices

For Cloud Engineering understand the Microsoft Azure Well-Architected Framework – Azure Architecture Center | Microsoft Docs. Microsoft Docs are rich. Read them. Use them.

Traceability

Always ensure your work is traceable (From Requirement to release). Link your PR to a ticket and ensure that the ticket number is in the git commit log. Discuss the best approach with your tech lead. Scaffolders will initiate the repository for you with an initial git commit and ticket samples along with documentation.

Integration

Understand the platform you are integrating with in order to reduce the code needed. For example, when working with Microsoft Dynamics, you could make use of its Plugins, PowerBI integration tools and other low-code tools that work out of the box. This will massively reduce the amount of code for your app if it runs on top of MS Dynamics.

Do not reinvent the wheel.

Let the platform take the strain, don’t write something that already exists. MS Dynamics, for example, uses entities for storing data. These entities are like database tables and can have a relationship between them. Use the built-in functions instead of introducing your own mechanism to link these entities together.

Security

Source Control Security

Where possible, use scanners to detect leaked credentials in source control. It has been noted that malicious bots scan source controls to gain access to resources by extracting passwords and tokens from source controls. Therefore, such information should NOT be stored in any git repository at all.

Use tools to help you

Scan all libraries with tools like CheckMarx and WhiteSource Bolt

Azure and AWS environment variables

Use environment variables to inject sensitive credentials, and keep them out of source control. Use key vaults to store certificates. Similar functionality is also available in Github but is more limited at the moment. Consult a DevOps engineer or security lead if not sure.

Sanitise input

Assume all input is invalid/malicious. Some languages such as .NET can handle this out of the box. However, be careful with frontend languages in particular. Users can skip frontend validation using the browser dev tools. Always perform these checks in the backend on top of the frontend platform.

Quality Attributes

Running Unit Tests

Don’t wait for someone else to test your code, it is your code and your responsibility. Make sure the code actually tests the acceptance criteria of your ticket. The pipeline should run unit tests after every commit. However, the code should be tested before it’s pushed to the origin. Write the code in good quality up to production code quality and ensure that it is readable, specific, and maintainable.

Bugs Detection

Catch bugs earlier to reduce the cost of defect management, the earlier it is detected, the cheaper it is to fix. Code should be effectively tested before it’s pushed for code review. Catching bugs is not only healthy because their presence is unwelcome but also because fixing them is circuitous. /testing

Static code analysis

Lint all code and always run static code analysis. Linters are great tools, especially for script languages. It gives hints and warnings as compilers do for compiled languages. Configure linters in advance in line with the project requirements and minimum standards. In addition, they are mostly extendable. Ie. you can create new custom rules specific to your case.

Backburner

Tackle technical debt as you find it, never leaves it for someone else Technical Debt. Sending the technical debt to the back burner will reduce the quality of the application by increasing the overhead. The scrum master and team have to monitor the technical debt and reassign it. By including the technical debt in tickets, you are helping your team and enhancing the quality. However, additional story points may need to be added to counter the additional work. 

Understand the meaning of shift left (Quality)

Testers shouldn’t be different from devs. Every developer should be able to write and run tests. Tests should be introduced as soon as possible to avoid creating a bottleneck at late stages before delivery/end of the sprint. Testers should join 3-amigos and contribute to ticket story pointing.

Add code analysers to your project and don’t ignore warnings

Whether these warnings are for unused variables or something more significant they should not be ignored. Keeping the codebase clean and tidy is not a bonus. This is essential to maintain quality.

Improve working Culture

Code quality and positive culture go hand in hand. Never underestimate the value of working in a positive culture and the impact it has on writing high-quality code.

Learn and share

The best method that verifies that you have actually learned something is by teaching it. Share your knowledge with others. It is the only thing that doubles by sharing.

Software Design Principles

Ensure a test can fail to prove it is not always providing a false positive

This is also valuable to ensure regression test is accurate. This is to ensure that bug fixes, and system configuration have not been impacted and the test passed.

Always strive to use the Red, Green, Refactor mantra.

Always write enough tests until it fails. do the fixes that you need to go from red (maybe expected) to green (sufficient testing) and refactor after.

Program to interfaces where possible, this will allow you to change the implementation without too much harm

This is part of SOLID principles. Interfaces are integrable with dependency injection. Therefore, changing a class implementation is done through configuration rather than code deletion and insertion.

Structure code in logical groupings, avoid one project with many layers, and separate layers based on domain / functional area.

Learn and Understand Design Patterns (GangOfFour)

Design patterns help you solve recurrent problems. For more information about using each pattern please refer to: GoF.

Follow SOLID & DRY & KISS Principles

For more info:

Follow Test Driven Development TDD

TDD is a gem. It helps identify areas of code which are not very well written and fix them. TDD aligns nicely with other principles referenced in this document. Therefore, it’s highly recommended that TDD is followed.

dotnet 3.1 onwards

Keep the version of .net core (if used in your project) up to date. Microsoft has introduced new features in .net and these features may not always be backwards compatible. Therefore, it’s recommended that you update the version to lates LTS. However, if this introduced any conflicts to run-time packages that you need to accomplish the task, then consult your tech lead.

Visual Studio 2019 onwards and VSCode versions

New VScode and VS support linters and refactoring out of the box. New IDEs are intelligent and can help you do your task more efficiently. Plugins support may vary so check which plugins you need for your project and discuss them with your team.

Object-Oriented Design

Favour composition over inheritance

Reduce Cost

  • Uniform Standards
  • Catch main issues early – monitoring and testing
  • Frictionless onboarding/talent across projects

Improves Quality & Agility

  • Improve Confidence
  • Release Faster
  • Build Strategic Products, not Tactical solutions
  • Learn and share
  • Break down Silos
  • Improve working Culture

Tooling and Versions

Supported Tooling

  • C# (Preferred Primary Language)
  • Typescript (for all UI)
  • VSCode
  • Visual Studio 2019 onwards
  • dotnet 3.1 onwards

Principles

Core Principles Every Developer should know

  • Follow Test Driven Development TDD
  • Always read the documentation
  • Don’t over-engineer (YAGNI)
  • Let the platform take the strain, don’t write something that already exists
  • Understand the platform you are integrating with in order to reduce the code needed
  • Always ensure your work is traceable (From Requirement to release)
  • Follow SOLID & DRY & KISS Principles
  • Learn and Understand Design Patterns (GangOfFour)
  • Care about all aspects of the delivery
  • Always ask why, for every single line of code
  • Understand the scope of the project, Don’t write code without knowing the bigger picture
  • Don’t ever say “It works on my machine”
  • Control your test data, don’t rely on external systems to make your tests work, and create your test data
  • Don’t rely on UI tests to create data, seed your own data
  • For Cloud Engineering understand the Microsoft Azure Well-Architected Framework – Azure Architecture Center | Microsoft Docs 
  • Assume all input is invalid/malicious,
  • Add code analysers to your project and don’t ignore warnings
  • Hide and façade complexity
  • Adopt a learning mindset – Culture Section
  • Adopt a DevOps mindset (Culture Section)
  • Understand the meaning of shift left (Quality)
  • Strive for good code coverage (Ideally 80% or above)
  • Use environment variables to inject sensitive credentials, and keep them out of source control
  • Structure code in logical groupings, avoid one project with many layers, and separate layers based on domain / functional area.
  • Program to interfaces where possible, this will allow you to change the implementation without too much harm
  • Favour composition over inheritance
  • Tackle technical debt as you find it, never leave it for someone else Technical Debt
  • Track technical debt and make it transparent in a backlog
  • Lint all code and always run static code analysis

Secure Coding Principles

  • Use environment variables to inject sensitive credentials, and keep them out of source control
  • Do not leave any sensitive information in log files e.g. connection strings or service names
  • Scan Container images and only use approved images from a trusted registry
  • Scan all libraries with tools like CheckMarx and WhiteSource Bolt
  • Where possible, use scanners to detect leaked credentials in source control

Testing Principles as a Developer

  • Strive to follow the Arrange, Act, Assert Pattern
  • Don’t write tests to cover third-party libraries, write tests to cover bespoke functionality
  • Ensure tests are run as part of a PR and are run for every single commit
  • Protect main / master branches with Unit, Integration and Acceptance tests where applicable
  • Run regular stress and performance tests early (Ideally in CI), do not wait until you are in production
  • Catch bugs earlier to reduce the cost of defect management, the earlier it is detected, the cheaper it is to fix
  • Don’t write tests just to up coverage, write them to prove the software is performing as expected
  • When you find code that is untested, write a test to cover it.
  • Don’t wait for someone else to test your code, it is your code and your responsibility
  • Always strive to use the Red, Green, Refactor mantra.
  • Ensure a test can fail to prove it is not always providing a false positive
  • Treat test code as you would with production code, it is not a second-hand citizen

Testing Considerations for CICD

  • Favour integration tests over UI / Acceptance tests where applicable, they are faster and easier to maintain
  • For Low code platforms such as Power Platform, unit testing is of little use, favour UI and Acceptance tests
  • Where possible run tests in parallel on the CICD build process to ensure faster execution and reduce queue time
Write a comment

Insert math as
Block
Inline
Additional settings
Formula color
Text color
#333333
Type math using LaTeX
Preview
\({}\)
Nothing to preview
Insert