Category Archives: CS-443

On Integrating Automated Testing

 In this post, I’ll be discussing my thoughts on a recent article I read from the Software Testing Help website, which can be found here. The piece really struck me because it reinforced many of the ideas I’ve come to believe about the role of testing in the software development lifecycle, particularly how automation can improve both speed and quality. I’ve always been a fan of automated testing, but this article helped me think more deeply about how it should fit into the broader testing strategy.

One of the key points in the article was the idea of balancing automation with manual testing. While automation is critical for repetitive tasks and quick feedback, the author pointed out that certain aspects of testing—like user experience—cannot be fully captured by automated scripts. This really resonated with me, as I’ve encountered situations where automation was great for catching functional issues, but it missed some of the nuance that a manual tester might be able to identify or spot. I think it’s a reminder that we should never rely too heavily on automation, and that human insight still has an important role to play.

In my own experience, automated testing has been a huge time-saver, especially for regression testing. It helps ensure that previously working functionality remains intact as new features are added. But I’ve also seen the limitations, particularly when automated tests don’t cover edge cases or fail to reflect real-world scenarios. I’ve learned that a good testing strategy needs to integrate both approaches—automation for efficiency and manual testing for critical thinking and creativity. I’ve gotten in the habit of mentally doing a once over to make sure that all my automated tests still cover everything I can think of, instead of just blindly assuming they do.

The article also emphasized the importance of writing testable code to support automation. This is something I think I can improve on in my own work. By considering testability from the start, we can avoid technical debt and create more maintainable, reliable systems. Writing code with testing in mind encourages good design practices and ensures that automated tests are effective.

Lastly, the article touched on continuous integration (CI) and how automated tests play a vital role in CI pipelines. This is something I’ve been trying to implement more consistently, and I’m seeing the value of catching bugs early, before they make it to production. It’s a mindset of constant improvement that aligns well with the idea of being a “Software Apprentice”—always refining and enhancing our process.

In conclusion, this article reaffirmed the importance of finding the right balance between automated and manual testing. As I continue my journey as a developer, I’ll be more mindful of how I integrate both into my workflow to ensure quality and efficiency.


From the blog Mr. Lancer 987's Blog by Mr. Lancer 987 and used with permission of the author. All other rights reserved by the author.

On Integrating Automated Testing

 In this post, I’ll be discussing my thoughts on a recent article I read from the Software Testing Help website, which can be found here. The piece really struck me because it reinforced many of the ideas I’ve come to believe about the role of testing in the software development lifecycle, particularly how automation can improve both speed and quality. I’ve always been a fan of automated testing, but this article helped me think more deeply about how it should fit into the broader testing strategy.

One of the key points in the article was the idea of balancing automation with manual testing. While automation is critical for repetitive tasks and quick feedback, the author pointed out that certain aspects of testing—like user experience—cannot be fully captured by automated scripts. This really resonated with me, as I’ve encountered situations where automation was great for catching functional issues, but it missed some of the nuance that a manual tester might be able to identify or spot. I think it’s a reminder that we should never rely too heavily on automation, and that human insight still has an important role to play.

In my own experience, automated testing has been a huge time-saver, especially for regression testing. It helps ensure that previously working functionality remains intact as new features are added. But I’ve also seen the limitations, particularly when automated tests don’t cover edge cases or fail to reflect real-world scenarios. I’ve learned that a good testing strategy needs to integrate both approaches—automation for efficiency and manual testing for critical thinking and creativity. I’ve gotten in the habit of mentally doing a once over to make sure that all my automated tests still cover everything I can think of, instead of just blindly assuming they do.

The article also emphasized the importance of writing testable code to support automation. This is something I think I can improve on in my own work. By considering testability from the start, we can avoid technical debt and create more maintainable, reliable systems. Writing code with testing in mind encourages good design practices and ensures that automated tests are effective.

Lastly, the article touched on continuous integration (CI) and how automated tests play a vital role in CI pipelines. This is something I’ve been trying to implement more consistently, and I’m seeing the value of catching bugs early, before they make it to production. It’s a mindset of constant improvement that aligns well with the idea of being a “Software Apprentice”—always refining and enhancing our process.

In conclusion, this article reaffirmed the importance of finding the right balance between automated and manual testing. As I continue my journey as a developer, I’ll be more mindful of how I integrate both into my workflow to ensure quality and efficiency.


From the blog Mr. Lancer 987's Blog by Mr. Lancer 987 and used with permission of the author. All other rights reserved by the author.

On Integrating Automated Testing

 In this post, I’ll be discussing my thoughts on a recent article I read from the Software Testing Help website, which can be found here. The piece really struck me because it reinforced many of the ideas I’ve come to believe about the role of testing in the software development lifecycle, particularly how automation can improve both speed and quality. I’ve always been a fan of automated testing, but this article helped me think more deeply about how it should fit into the broader testing strategy.

One of the key points in the article was the idea of balancing automation with manual testing. While automation is critical for repetitive tasks and quick feedback, the author pointed out that certain aspects of testing—like user experience—cannot be fully captured by automated scripts. This really resonated with me, as I’ve encountered situations where automation was great for catching functional issues, but it missed some of the nuance that a manual tester might be able to identify or spot. I think it’s a reminder that we should never rely too heavily on automation, and that human insight still has an important role to play.

In my own experience, automated testing has been a huge time-saver, especially for regression testing. It helps ensure that previously working functionality remains intact as new features are added. But I’ve also seen the limitations, particularly when automated tests don’t cover edge cases or fail to reflect real-world scenarios. I’ve learned that a good testing strategy needs to integrate both approaches—automation for efficiency and manual testing for critical thinking and creativity. I’ve gotten in the habit of mentally doing a once over to make sure that all my automated tests still cover everything I can think of, instead of just blindly assuming they do.

The article also emphasized the importance of writing testable code to support automation. This is something I think I can improve on in my own work. By considering testability from the start, we can avoid technical debt and create more maintainable, reliable systems. Writing code with testing in mind encourages good design practices and ensures that automated tests are effective.

Lastly, the article touched on continuous integration (CI) and how automated tests play a vital role in CI pipelines. This is something I’ve been trying to implement more consistently, and I’m seeing the value of catching bugs early, before they make it to production. It’s a mindset of constant improvement that aligns well with the idea of being a “Software Apprentice”—always refining and enhancing our process.

In conclusion, this article reaffirmed the importance of finding the right balance between automated and manual testing. As I continue my journey as a developer, I’ll be more mindful of how I integrate both into my workflow to ensure quality and efficiency.


From the blog Mr. Lancer 987's Blog by Mr. Lancer 987 and used with permission of the author. All other rights reserved by the author.

Test-driven Development

URL: https://semaphoreci.com/blog/test-driven-development
The blog in question was written by Ferdinando Santacroce on Sephamore. The title is Test-Driven Development (TDD): A Time-Tested Recipe for Quality Software. He walks his readers through many topics related to Test-Driven Development. These topics include TDD as a design practice, TDD as a well-established engineering practice, and many others.

Test-Driven Development caught my attention because it implies that the inverse of traditional testing should be done. Instead of writing tests for existing code, you should rather write tests for code that has yet to be written. This is confusing in some ways and also misleading when considering only the phrase “Test-Driven Development.” However, as we all know, every concept means more than just the words it comprises. And TDD (or Test-Driven Development) follows the same rule—it is much more than simply writing tests first.

I would personally call it a way of thinking or a different point of view on how tests and code are written. TDD resembles Agile and Scrum’s approach to software development. It shifts the developer’s focus from simply producing and creating large amounts of code—where everything eventually becomes one big entity—to a much more structured and manageable process. As Agile methodology suggests, it is better to build, review, test, and repeat. This method, combined with Scrum, takes this perspective further. Small chunks of functionality are built in shorter time frames, allowing for continuous review and feedback.

TDD applies the same principle with one key difference: test first—or at least, write your tests first. By writing down what should be tested and what a program should return or produce, you now have a clear goal. This method shifts the traditional way of thinking and provides a clear path to the main objective. This objective is then broken down into many smaller pieces that are easier to understand. Another benefit of TDD is the constant feedback, often called a “pleasing side effect.” Continuously receiving feedback on your code gives you a steady sense of accomplishment.

The outcomes of introducing Test-Driven Development into one’s development process can bring many benefits. However, beneficial outcomes are not guaranteed for every single person who applies this methodology. That said, I would argue that certain developers may benefit more than others. Developers who often feel lost, confused, or disoriented may benefit greatly from Test-Driven Development. This approach provides them with a clear understanding of the code’s structure in a more modular and organized way. By separating the code’s functions into different sections, building blocks, or containers, TDD creates a more structured and efficient development environment.

From the blog CS@Worcester – CS Today by Guilherme Salazar Almeida Nazareth and used with permission of the author. All other rights reserved by the author.

The Intersection of Manual Testing, Automated Testing, Equivalence Class Testing, and Security Testing

Hello guys, welcome to my second blog. In my previous blog, I explored JUnit testing, focusing on boundary value analysis and how to use assert Throws for exception handling in Java. Those concepts helped us understand how to write robust test cases and ensure that our software behaves correctly at its limits.

Building on that foundation, this post expands into a broader discussion of manual vs. automated testing, equivalence class testing, and security testing. These methodologies work together to create a comprehensive testing strategy that ensures software is functional, efficient, and secure.

Introduction

Software testing is a critical part of the software development lifecycle (SDLC). Among the various testing methodologies, manual testing, automated testing, equivalence class testing, and security testing play vital roles in ensuring software reliability, performance, and security. This article explores these techniques, their strengths, and how they work together.

Manual vs. Automated Testing

Testing software can be broadly categorized into manual testing and automated testing. Each approach serves specific purposes and has its advantages.

Manual Testing

Manual testing involves human testers executing test cases without automation tools. It is useful for exploratory testing, usability testing, and cases requiring human judgment.

Pros:

  • Suitable for UI/UX testing and exploratory testing
  • Requires no programming skills
  • Provides a human perspective on software quality

Cons:

  • Time-consuming and repetitive
  • Prone to human errors
  • Inefficient for large-scale projects

Automated Testing

Automated testing uses scripts and testing frameworks to execute test cases automatically. It is ideal for regression testing and performance testing.

Pros:

  • Faster execution and scalable for large projects
  • Reduces human errors
  • Works well with continuous integration/continuous deployment (CI/CD)

Cons:

  • High initial setup cost
  • Requires programming expertise
  • Not suitable for exploratory testing

Example: Java Automated Test Using Selenium

Here is a basic example of using Java to automate a test: I have a simple Calculator class and an Add method. There is a test that runs without human intervention once executed

public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
}

import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;

public class CalculatorTest {
    @Test
    public void testAddition() {
        Calculator calculator = new Calculator();
        int result = calculator.add(2, 3);
        assertEquals(5, result); 
    }
}

Equivalence Class Testing

Equivalence Class Testing (ECT) is a black-box testing technique that reduces the number of test cases while ensuring comprehensive test coverage. It divides input data into equivalence classes, where each class represents similar expected outcomes.

How It Works:

  • Identify input conditions
  • Categorize inputs into equivalence classes (valid and invalid)
  • Select one representative input from each class for testing

Example: Equivalence Class Testing in Java

Consider a function that validates age input:

public class AgeValidator {
    public static String validateAge(int age) {
        if (age >= 18 && age <= 60) {
            return "Valid Age";
        } else {
            return "Invalid Age";
        }
    }

    public static void main(String[] args) {
        System.out.println(validateAge(25)); // Valid input class
        System.out.println(validateAge(17)); // Invalid input class
        System.out.println(validateAge(61)); // Invalid input class
    }
}

Security Testing: A Critical Component

Security testing ensures that applications are protected against cyber threats. This is increasingly crucial with rising cyberattacks and data breaches.

Key Security Testing Techniques:

  • Penetration Testing: Simulating cyberattacks to identify vulnerabilities.
  • Static Code Analysis: Reviewing source code for potential security flaws.
  • Dynamic Analysis: Testing a running application for security weaknesses.
  • Fuzz Testing: Inputting random data to identify unexpected behavior.

Example: Basic SQL Injection Test

A common security vulnerability is SQL Injection. Here’s an example of a vulnerable and a secure implementation:

Vulnerable Code:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class SQLInjectionVulnerable {
    public static void getUserData(String userId) {
        try {
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password");
            Statement stmt = conn.createStatement();
            String query = "SELECT * FROM users WHERE id = " + userId; // Vulnerable to SQL Injection
            ResultSet rs = stmt.executeQuery(query);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Secure Code:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class SQLInjectionSecure {
    public static void getUserData(String userId) {
        try {
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password");
            String query = "SELECT * FROM users WHERE id = ?";
            PreparedStatement pstmt = conn.prepareStatement(query);
            pstmt.setString(1, userId);
            ResultSet rs = pstmt.executeQuery();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

How These Testing Methods Work Together

Each testing approach contributes uniquely to software quality:

  • Manual and automated testing ensure functional correctness and usability.
  • Equivalence class testing optimizes test case selection.
  • Security testing safeguards applications from threats.

By integrating these methods, development teams can achieve efficiency, reliability, and security in their software products.

Conclusion

A strong testing strategy incorporates manual testing, automated testing, equivalence class testing, and security testing. Leveraging these approaches ensures robust software quality while improving development efficiency.

For further exploration, consider:

From the blog Rick’s Software Journal by RickDjouwe1 and used with permission of the author. All other rights reserved by the author.

Unit Testing and Testable Code


Unit testing is a fundamental practice in software development, ensuring that individual units of code work as expected. However, the real challenge often lies in writing code that is easy to test. Poorly designed, untestable code can complicate unit testing and introduce expensive complexity. In this blog post, we’ll explore the importance of writing testable code, the common pitfalls that make code hard to test, and the benefits of adopting testable coding practices.

The Significance of Unit Testing

Unit testing involves verifying the behavior of a small portion of an application independently from other parts. A typical unit test follows the Arrange-Act-Assert (AAA) pattern: initializing the system under test, applying a stimulus, and observing the resulting behavior. The goal is to ensure that the code behaves as expected and meets the specified requirements.

However, the ease of writing unit tests is significantly influenced by the design of the code. Code that is tightly coupled, non-deterministic, or dependent on mutable global state is inherently difficult to test. Writing testable code is not only about making testing less troublesome but also about creating robust and maintainable software.

Common Pitfalls in Writing Testable Code

Several factors can make code challenging to test, including:

  1. Tight Coupling: Code that is tightly coupled to specific implementations or data sources is difficult to isolate for testing. Decoupling concerns and introducing clear seams between components can enhance testability.
  2. Non-Deterministic Behavior: Code that depends on mutable global state or external factors (e.g., current system time) can produce different results in different environments, complicating testing. Making code deterministic by injecting dependencies can address this issue.
  3. Side Effects: Methods that produce side effects (e.g., interacting with hardware or external systems) are hard to test in isolation. Employing techniques like Dependency Injection or using higher-order functions can help in decoupling and testing such code.

Benefits of Testable Code

Adopting testable coding practices offers several benefits:

  1. Improved Code Quality: Testable code is typically well-structured, modular, and easier to understand. This leads to higher code quality and reduces the likelihood of bugs.
  2. Easier Maintenance: Code that is easy to test is also easier to maintain. Changes can be made with confidence, knowing that unit tests will catch any regressions.
  3. Faster Development: With a robust suite of unit tests, developers can iterate quickly and confidently, reducing the time spent on manual testing and debugging.
  4. Enhanced Collaboration: Clear and testable code promotes better collaboration among team members, as the intent and behavior of the code are easier to comprehend.

Conclusion

Writing testable code is a crucial aspect of software development that extends beyond the realm of testing. It encompasses good design principles, decoupling, and the elimination of non-deterministic behavior and side effects. By focusing on writing testable code, developers can create software that is not only easier to test but also more robust, maintainable, and reliable. Embracing these practices ultimately leads to higher quality software and more efficient development processes.

All of this comes from the link below:

https://www.toptal.com/qa/how-to-write-testable-code-and-why-it-matters


From the blog CS@Worcester – aRomeoDev by aromeo4f978d012d4 and used with permission of the author. All other rights reserved by the author.

Always Improving Software

In this post, I’ll be discussing my thoughts primarily on Chapter 1 of Apprenticeship Patterns, a book I had to read parts of for another class. I really like the idea of being a “Software Apprentice,” as I think it is a good representation of the attitude I have adopted over the years. I’ve always viewed myself as someone who is constantly learning and evolving in this field, and the apprenticeship model seems to align perfectly with that. It’s not about arriving at some endpoint but about growing, making mistakes, and improving.

What stood out to me the most was the idea of viewing the development process as a journey, not a destination. Chapter 1 introduces the concept of “Software Craftsmanship” as more than just technical skill, but also as a mindset of continuous learning. This was particularly thought-provoking because I think there’s a tendency, especially early in our careers, to think of coding as a task to be completed or a skill to be mastered and then moved beyond while in pursuit of some computer science career. However, the idea that becoming a Software Craftsman is an ongoing process—a commitment to constant improvement and a mindset that thrives on learning—is something that I think is a better description. It shifted my perspective on what it means to be good at coding- it’s not enough to know how to do something today, it’s about always finding a way to improve your code in the future.

In the past, specifically in my career, I’ve often gotten caught up in the day-to-day rush of just getting whatever piece of code done. I’ve rushed to get things coded, with the focus being on completing projects rather than developing deeper expertise or refining my approach to coding. After reading this chapter, I think I’m more likely to focus on the long-term value of writing quality code and the importance of standards. It’s not just about writing code that works; it’s about writing code that lasts, is readable, and contributes to a more sustainable project. I’m going to start to approach my coding tasks with a deeper goal, rather than rushing through them for the sake of completion.

I think this is particularly applicable to Software Quality Assurance and Testing because QA is, in essence, a form of ensuring that our code is always improving. Not only do we need to find a balance between constantly improving before release and actually releasing, but even after releases, we still need to be looking for future requirements and improvements. There is no end in sight for software testing, ever. I think this constant forward motion, driven by constantly increasing requirements, is inseparable from the idea of being a software apprentice.

From the blog Mr. Lancer 987&#039;s Blog by Mr. Lancer 987 and used with permission of the author. All other rights reserved by the author.

Always Improving Software

In this post, I’ll be discussing my thoughts primarily on Chapter 1 of Apprenticeship Patterns, a book I had to read parts of for another class. I really like the idea of being a “Software Apprentice,” as I think it is a good representation of the attitude I have adopted over the years. I’ve always viewed myself as someone who is constantly learning and evolving in this field, and the apprenticeship model seems to align perfectly with that. It’s not about arriving at some endpoint but about growing, making mistakes, and improving.

What stood out to me the most was the idea of viewing the development process as a journey, not a destination. Chapter 1 introduces the concept of “Software Craftsmanship” as more than just technical skill, but also as a mindset of continuous learning. This was particularly thought-provoking because I think there’s a tendency, especially early in our careers, to think of coding as a task to be completed or a skill to be mastered and then moved beyond while in pursuit of some computer science career. However, the idea that becoming a Software Craftsman is an ongoing process—a commitment to constant improvement and a mindset that thrives on learning—is something that I think is a better description. It shifted my perspective on what it means to be good at coding- it’s not enough to know how to do something today, it’s about always finding a way to improve your code in the future.

In the past, specifically in my career, I’ve often gotten caught up in the day-to-day rush of just getting whatever piece of code done. I’ve rushed to get things coded, with the focus being on completing projects rather than developing deeper expertise or refining my approach to coding. After reading this chapter, I think I’m more likely to focus on the long-term value of writing quality code and the importance of standards. It’s not just about writing code that works; it’s about writing code that lasts, is readable, and contributes to a more sustainable project. I’m going to start to approach my coding tasks with a deeper goal, rather than rushing through them for the sake of completion.

I think this is particularly applicable to Software Quality Assurance and Testing because QA is, in essence, a form of ensuring that our code is always improving. Not only do we need to find a balance between constantly improving before release and actually releasing, but even after releases, we still need to be looking for future requirements and improvements. There is no end in sight for software testing, ever. I think this constant forward motion, driven by constantly increasing requirements, is inseparable from the idea of being a software apprentice.

From the blog Mr. Lancer 987&#039;s Blog by Mr. Lancer 987 and used with permission of the author. All other rights reserved by the author.

Always Improving Software

In this post, I’ll be discussing my thoughts primarily on Chapter 1 of Apprenticeship Patterns, a book I had to read parts of for another class. I really like the idea of being a “Software Apprentice,” as I think it is a good representation of the attitude I have adopted over the years. I’ve always viewed myself as someone who is constantly learning and evolving in this field, and the apprenticeship model seems to align perfectly with that. It’s not about arriving at some endpoint but about growing, making mistakes, and improving.

What stood out to me the most was the idea of viewing the development process as a journey, not a destination. Chapter 1 introduces the concept of “Software Craftsmanship” as more than just technical skill, but also as a mindset of continuous learning. This was particularly thought-provoking because I think there’s a tendency, especially early in our careers, to think of coding as a task to be completed or a skill to be mastered and then moved beyond while in pursuit of some computer science career. However, the idea that becoming a Software Craftsman is an ongoing process—a commitment to constant improvement and a mindset that thrives on learning—is something that I think is a better description. It shifted my perspective on what it means to be good at coding- it’s not enough to know how to do something today, it’s about always finding a way to improve your code in the future.

In the past, specifically in my career, I’ve often gotten caught up in the day-to-day rush of just getting whatever piece of code done. I’ve rushed to get things coded, with the focus being on completing projects rather than developing deeper expertise or refining my approach to coding. After reading this chapter, I think I’m more likely to focus on the long-term value of writing quality code and the importance of standards. It’s not just about writing code that works; it’s about writing code that lasts, is readable, and contributes to a more sustainable project. I’m going to start to approach my coding tasks with a deeper goal, rather than rushing through them for the sake of completion.

I think this is particularly applicable to Software Quality Assurance and Testing because QA is, in essence, a form of ensuring that our code is always improving. Not only do we need to find a balance between constantly improving before release and actually releasing, but even after releases, we still need to be looking for future requirements and improvements. There is no end in sight for software testing, ever. I think this constant forward motion, driven by constantly increasing requirements, is inseparable from the idea of being a software apprentice.

From the blog Mr. Lancer 987&#039;s Blog by Mr. Lancer 987 and used with permission of the author. All other rights reserved by the author.

Always Improving Software

In this post, I’ll be discussing my thoughts primarily on Chapter 1 of Apprenticeship Patterns, a book I had to read parts of for another class. I really like the idea of being a “Software Apprentice,” as I think it is a good representation of the attitude I have adopted over the years. I’ve always viewed myself as someone who is constantly learning and evolving in this field, and the apprenticeship model seems to align perfectly with that. It’s not about arriving at some endpoint but about growing, making mistakes, and improving.

What stood out to me the most was the idea of viewing the development process as a journey, not a destination. Chapter 1 introduces the concept of “Software Craftsmanship” as more than just technical skill, but also as a mindset of continuous learning. This was particularly thought-provoking because I think there’s a tendency, especially early in our careers, to think of coding as a task to be completed or a skill to be mastered and then moved beyond while in pursuit of some computer science career. However, the idea that becoming a Software Craftsman is an ongoing process—a commitment to constant improvement and a mindset that thrives on learning—is something that I think is a better description. It shifted my perspective on what it means to be good at coding- it’s not enough to know how to do something today, it’s about always finding a way to improve your code in the future.

In the past, specifically in my career, I’ve often gotten caught up in the day-to-day rush of just getting whatever piece of code done. I’ve rushed to get things coded, with the focus being on completing projects rather than developing deeper expertise or refining my approach to coding. After reading this chapter, I think I’m more likely to focus on the long-term value of writing quality code and the importance of standards. It’s not just about writing code that works; it’s about writing code that lasts, is readable, and contributes to a more sustainable project. I’m going to start to approach my coding tasks with a deeper goal, rather than rushing through them for the sake of completion.

I think this is particularly applicable to Software Quality Assurance and Testing because QA is, in essence, a form of ensuring that our code is always improving. Not only do we need to find a balance between constantly improving before release and actually releasing, but even after releases, we still need to be looking for future requirements and improvements. There is no end in sight for software testing, ever. I think this constant forward motion, driven by constantly increasing requirements, is inseparable from the idea of being a software apprentice.

From the blog Mr. Lancer 987&#039;s Blog by Mr. Lancer 987 and used with permission of the author. All other rights reserved by the author.