paint-brush
Software Development Lifecycle: A Useful Guideby@pro1code1hack
224 reads

Software Development Lifecycle: A Useful Guide

by Yehor DremliuhaJuly 29th, 2024
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Software Development Lifecycle (SDLC) is crucial for creating successful software. It provides a clear plan and structure for each step, helping teams stay organized. Once you start following SDLC, I promise that your projects will become unrecognisable.
featured image - Software Development Lifecycle: A Useful Guide
Yehor Dremliuha HackerNoon profile picture

1. Introduction

When I started developing my pet projects, I had a problem with having a consistent system that could help me get a detailed step-by-step guide to implement the project from an idea.


Then I got acquainted with the Software Development Lifecycle (SDLC) and started to implement my own practices. My primary language is Python, and I will tell you the exact workflow of how I usually develop applications in this language.


The SDLC is crucial for creating successful software. It provides a clear plan and structure for each step, helping teams stay organized. Once you start following SDLC, I promise that your projects will become absolutely unrecognizable.


It requires several steps that should be completed, and let’s jump into them immediately:


Figure 1- SDLC (Software Development Lifecycle)


I am a more practical person than a theoretical one, and I would suggest creating an application on practice applying SDLC principles.

2. Application Overview

We'll create a "Recipe Recommender" application that suggests recipes based on the ingredients users have.


In order not to make this article too overwhelming, let’s create a simple CLI version, and you will be able to implement the backend and frontend following absolutely the same steps and SDLC approach described in this article.

3. Planning

In the planning phase, the focus is mainly on Gathering Requirements and Setting Estimates.

3.1 Gathering Requirements:

  • Users need to input available ingredients.
  • The application should suggest recipes that can be made with those ingredients.
  • Users should be able to view recipe details including ingredients and instructions.


IMPORTANT: The developer must define precise and well-described requirements. It is crucial to understand the needed functionality and how users will interact with the application. Be as specific as possible!

3.2 Setting Estimates:

While setting estimates, I would suggest you break down a big issue into smaller chunks and analyze how much time each small issue will take. Avoid being too strict, and provide your team with a few extra days for development. It’s a rare occasion when everything goes according to the plan in software development 😉.


  • Development time: 2 weeks.
  • Testing time: 3 days.

4. Design

The design stage is one of the most complicated aspects of the SDLC. Developers should understand that each made decision during this stage can significantly impact the project's success. It contains the following steps:


  1. High-Level Design (System Architecture):
  2. Low-Level Design (Component and Module Details):
  3. User-Flow (How the user interacts with an application)

4.1 High-Level Design

Usually, in this case, you create a diagram of architecture, and follow the checklist you can see in the screenshot below:

Figure 2 - High-Level Design checklists

This checklist may vary depending on the technologies being used. But, generally, it is the most abstract representation which can be applicable for the majority of the projects.


For our application we will need the following:


  • A script with functions for ingredient input, recipe suggestions, and displaying recipe details.


  • A simple database (dictionary) of recipes.



4.2 Low-Level Design:

Break down the project into clear modules or components based on functionality. This stage requires the following:


  • The structure of the project.


  • Functions: input_ingredients, suggest_recipes, view_recipe_details.


  • Data Storage: Dictionary to store recipes and their details.


For simplicity, I’ve come up with the following structure:

recipe_recommender/
├── utils.py		# user input
├── recipes.py		# mini-db and logic of suggestion
└── main.py             # put it altogether 


4.3 The User Flow

This stage is the most important in my opinion, you just put yourself in the user’s place, and think about how you would interact with an application. Users don’t care about the code quality or the chosen programming language; they want to have a ready-to-use product!


Figure 3 - User Flow


5. Implementation

In this part, you bring the idea to life! The most favorite part of programmers. My recommendation will be to follow the best practices known in the world of software engineering during the implementation.


# utils.py

def input_ingredients():
    ingredients = input("Enter your available ingredients, separated by commas: ").lower().split(", ")
    return ingredients


# recipes.py

recipes = {
    "Pasta Salad": {
        "ingredients": ["pasta", "tomatoes", "olive oil", "salt"],
        "instructions": "Boil pasta, chop tomatoes, mix with olive oil and salt. Serve chilled."
    },
    "Omelette": {
        "ingredients": ["eggs", "salt", "pepper", "cheese"],
        "instructions": "Beat eggs, add salt and pepper, cook on a skillet, add cheese before folding."
    }
}

def suggest_recipes(available_ingredients):
    suggested_recipes = []
    for recipe, details in recipes.items():
        if all(item in available_ingredients for item in details["ingredients"]):
            suggested_recipes.append(recipe)
    return suggested_recipes

def view_recipe_details(recipe_name):
    if recipe_name in recipes:
        print(f"\nRecipe for {recipe_name}:")
        print("Ingredients:", ", ".join(recipes[recipe_name]["ingredients"]))
        print("Instructions:", recipes[recipe_name]["instructions"])
    else:
        print("Recipe not found.")


# main.py

from recipes import suggest_recipes, view_recipe_details
from utils import input_ingredients

def main():
    while True:
        print("\nRecipe Recommender Application")
        print("1. Input Ingredients")
        print("2. Suggest Recipes")
        print("3. View Recipe Details")
        print("4. Exit")
        
        choice = input("Choose an option: ")
        
        if choice == '1':
            available_ingredients = input_ingredients()
            print("Ingredients received.")
        elif choice == '2':
            suggested_recipes = suggest_recipes(available_ingredients)
            if suggested_recipes:
                print("Recipes you can make:", ", ".join(suggested_recipes))
            else:
                print("No recipes found with the given ingredients.")
        elif choice == '3':
            recipe_name = input("Enter the recipe name: ")
            view_recipe_details(recipe_name)
        elif choice == '4':
            break
        else:
            print("Invalid choice, please try again.")

if __name__ == "__main__":
    main()


6. Testing

During this part, you MUST write tests to ensure that the application works as expected. As well as each piece of new functionality should be tested E2E before moving to the development of new features. The applications must be covered with all types of tests including Unit Testing, Integration Testing, and E2E Testing.

6.1 Unit Testing

Testing individual functions and modules to ensure they work correctly in isolation.

import unittest
from recipes import suggest_recipes, view_recipe_details

class TestRecipes(unittest.TestCase):
    
    def setUp(self):
        self.available_ingredients = ["pasta", "tomatoes", "olive oil", "salt"]
        self.missing_ingredients = ["pasta", "tomatoes"]
    
    def test_suggest_recipes(self):
        expected_output = ["Pasta Salad"]
        self.assertEqual(suggest_recipes(self.available_ingredients), expected_output)
        self.assertEqual(suggest_recipes(self.missing_ingredients), [])

6.2 Integration Testing

Here, you want to ensure that the whole interaction across modules is being consistent and works according to the desired logic.

class TestRecipeRecommender(unittest.TestCase):
    
    @patch('builtins.input', side_effect=['1', 'eggs, cheese, salt', '2', '3', 'Omelette', '4'])
    @patch('sys.stdout', new_callable=StringIO)
    def test_full_integration(self, mock_stdout, mock_input):
        main.main()
        output = mock_stdout.getvalue().strip()
        self.assertIn("Ingredients received.", output
        self.assertIn("Recipes you can make: Omelette", output)   

6.3 E2E Testing

The easiest part of this workflow, act as you are just playing with an app as a user trying to ensure that it works as expected or breaks it.

Recipe Recommender Application
1. Input Ingredients
2. Suggest Recipes
3. View Recipe Details
4. Exit
Choose an option: 1    
Enter your available ingredients, separated by commas: pasta, tomatoes, olive oil, salt
Ingredients received.


Try to break your application, identify the pain points, and fix them during the refactoring/maintenance stages. ⬇️

7. Maintenance

Before the maintenance part, you should deploy your application to make it accessible for the users. I will not focus on this, as it is too huge to cover, but my personal recommendation would be to use services like Heroku/AWS/GCP. The maintenance part is crucial as well; here, we do the following:


Figure 5 - Maintenance Loop


7.1 Performance and Usage

This is usually handled by the server providers, and you don’t need to do anything. If your application produces a bad performance, you need to either increase the resources of the server or refactor adding asynchronous functionality. Use profilers such as scalene and refactor complex logic that eats your resources.


For additional monitoring, it would be nice to set up Grafana or similar tools.

Figure 4 - Grafana for Monitoring

7.2 Logging

We haven’t added logging during development, and that resulted in problems with tracking how users interact with an application so, it is not possible to track potential bugs and errors in case the application is breaking.


Add the following functions, try catch clauses to the app, and log each crucial section where the interaction happening:

import logging

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

def log_input(ingredients):
    logging.info(f"User input ingredients: {ingredients}")

def log_suggestions(suggestions):
    logging.info(f"Suggested recipes: {suggestions}")

def log_error(error_message):
    logging.error(f"Error: {error_message}")

log_input(["pasta", "tomatoes", "olive oil", "salt"])
log_suggestions(["Pasta Salad"])
log_error("Recipe not found.")


After implementation of this, you will be able to see if anything went wrong with your app. Fix bugs and respond immediately.

7.3 User Feedback

Now, you would like to contact users directly, and ask what they like or don’t like about your application. Ask people to provide feedback, as you have created this software for them. You want to know what functionality people want to be implemented. And how to improve their overall experience with an app.


Suppose we have the following request:

Hi there!


I really like your application!  I would like to have an option to add my own recipes to the database and have them included into suggestions!


Best Regards!


Alright, your wish is our command! Let’s start development! And since we are professionals already, we will follow the software development lifecycle! Planning -> Design -> Implementation -> Testing -> Maintenance -> Improvements

8. Conclusion

Thanks a lot for your attention. I hope that you have got the general idea of developing a high-quality software. As It was mentioned before, I decided to simplify the application in order to have a readable article.


For more complex applications, you need to plan the architecture of high-level components. For instance, how FE and BE interact with each other, the structure of the database, deployment pipelines, and, of course, more complex user flow.


Try the SDLC approach described in this article but be extremely careful, as it can lead to high-quality software 🙂. Best of luck!