Category Archives: CS-343

What Is Docker?

And How is it Useful?

As I work on more and more assignments I am constantly hit with the same error while starting up my work for class:

The fix to this is very simple; Start up Docker. This error simply means that my VS Code is attempting to open a container, but I don’t have Docker running to do that. As this reminder started to get me in the habit of making sure Docker was open before attempting to work in a container, I started to ask myself, “What is Docker actually doing and what is a container?”.

I found an excellent post that breaks down what Docker is and how each of the individual parts of Docker work together. The blog, “Introduction to Containers and Docker” by Liam Mooney (https://endjin.com/blog/2022/01/introduction-to-containers-and-docker) provides great information of how Docker used containers with an example of a dockerfile and how to build your own container.  Mooney starts off with explaining what a container is and how they vary from virtual machines. Containers and virtual machines are both ways of creating an isolated environment on a computer, however, virtual machines are bulky and slow in comparison to containers. This means they can both be used to create stable environments to run software in, however, virtual machines require an image of an OS to be installed on a host computer. This OS also needs to be installed every time you start the virtual machine. This paired with taking up a lot of space and CPU resources by having two OS’s that share a lot of features leads to much longer start up times and slower runtime. Containers use the capabilities and features of the host OS to run the environments. This makes them much lighter, only needing select software and dependencies to be included in the environment.

This blog goes on to explain how Docker is able to use containers to create environments in seconds. Docker uses dockfiles, which are a list of commands the docker daemon will execute to build the image you wish to run for your environment. Docker daemon is a background service that builds, manages, and runs the environment by being the middleman between the container and the host OS. Once the image has been built, it can be opened in any number of containers that are all independent of each other.

The examples given by Mooney are great for understanding exactly how you would create a simple container. Giving me a better look at what Docker is actually doing when I am working in VS code and its opening containers for me to work in. Although I don’t see myself designing my own containers anytime soon, it is great to know how the software is executing and managing these environments.

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

What is Refactoring?

Refactoring is the process of restructuring a code without changing or adding to its functionality and external behavior. There are a lot of ways to go about refactoring but it mostly goes towards applying standard basic actions. These changes in the existing code save the software’s functionality and behavior since the changes are so tiny, they are less likely to cause any errors. So, what is the point of refactoring? Well, refactoring is to turn any messy confusing code into a clean understandable one instead. When a code is messy it means that the code is hard to understand and maintain, when it’s time to add a required functionality it causes a lot of problems with the code because it’s confusing already. With a clean code, it’s easier to make any changes and improve on any problems. Also with a clean code anybody who ever works with the code is able to understand the code and can appreciate how organized it is. When a messy code isn’t cleaned up it can affect any feature developments because developers have to take more time to understand the code and track the code so that they can make any changes themselves.

Knowing when to refactor is important and there are different times to refactor your code. Like refactoring, while you’re reviewing the code, reviewing the code before it goes live is the best time to refactor and make any changes you can before pushing it through. You can also schedule certain parts of your day to refactor your code instead of doing it all at once. By cleaning your code you are able to catch any bugs before they create any problems in your code. The main thing about refactoring your code is that cleaning up a dirty code can reduce technical debt. Clean code is easier to read and if anybody else besides the developer works on that code they are also able to easily understand that code as well as maintain and add features to it. The less complicated the code is, it can lead to improved source-code maintenance. With a clean code, the design can be used as a tool for any other developers it can become the basis of a code elsewhere. This is why I believe that refactoring is important because changing just the smallest piece of code can lead to a better functional approach to programming. It helps developers get a better understanding of the code as well as making better design decisions.

From the blog CS@Worcester – Kaylene Noel's Blog by Kaylene Noel and used with permission of the author. All other rights reserved by the author.

Docker and Dev Containers – Understanding the Basics

Over the past few weeks, our in-class activities as well as our last assignment for CS-343 have required running Docker in conjunction with a Dev Container in VSCode. While I was able to follow the instructions given and complete our assignments using these tools, I struggled getting started and navigating through some of the components for in-class activities at times, impeding my ability to work efficiently and keep up with my team. To make things worse, I didn’t have much of an understanding of what each component did and why it’s being used. So, I decided to do some research on these topics, particularly the relevance, benefits and drawbacks of using Dev Containers and how they are connected to VSCode and Docker.

In my search, I found Getting Started With Dev Containers, a blog post by CS professional Dave Storey which clarifies a lot, beginning with addressing why Dev Containers are used. Setting up development environments can be a lengthy and tedious process depending on prerequisites, dependencies or other implementations. Dev Containers utilize the power of containerization – bundling all files, SDKs etc. needed to run an application for several benefits like lower overhead, portability, consistency, and more. 

There are some general advantages and disadvantages of using Dev Containers to be aware of. For starters, they guarantee a consistent development environment regardless of the hardware/operating system being used as long as it can run the container. Similarly, there’s guaranteed consistency across toolsets that each teammate is using, so everyone is familiar and there is no communication or project friction caused by inconsistent setups or toolkits. It’s also quick and easy to integrate a new member to a team/project by instantly setting them up with the same development environment in use by the rest of the team. Dev Containers are reusable and adaptable for other projects, providing long term value by saving time on project set-up.

While there’s lots of advantages, there are also drawbacks that should be considered when strategizing projects and contemplating the use of Dev Containers. There’s an upfront time/cost barrier in setting up the container and its configurations – though available templates make this process easier. To this point, you need to have a basic understanding of Docker and general containerization to set up and maximize benefits from Dev Containers. And, when dependencies and other components become deprecated, maintenance needs to be done to make sure that Dev Containers are up-to-date and usable as they are (usually) not part of main code repositories. 

This helped to clarify things based on what we’ve worked with in class. Dev Containers are using VSCode like an IDE/server which communicates with Git and runs our code in the Docker container. Our implementation in class lets everyone deal with the same tools and environments to leverage these mentioned benefits for group learning projects as well as individual assignments.

Sources:

  1. Getting Started With Dev Containers | by C:\Dave\Storey | Medium
  2. What is a software development kit (SDK)? – Definition from TechTarget

From the blog CS@Worcester – Tech. Worth Talking About by jelbirt and used with permission of the author. All other rights reserved by the author.

CS343 Blog Post for Week of November 30, 2023

This week, I wanted to continue writing about the SOLID software design principles. The next principle I have to cover is the Liskov substitution principle. This principle expands upon the Open/Closed principle I wrote about the previous week, providing a guideline on how a superclass and its child classes should behave.

This principle was first defined by Barbara Liskov in 1987, and later adopted by Robert C. Martin. From a paper that Barbara Liskov cowrote with Jeanette Wing, they defined the Liskov substitution principle as a mathematical property: “Let Φ(x) be a property provable about objects x of type T. Then Φ(y) should be true for objects y of type S where S is a subtype of T.”

Translated into plain English, this principle states that instances of a superclass in a program should be able to be replaced by one of its subclasses without breaking the program. Overridden methods from a subclass must accept the same input as the superclass’s original method, and the return value of a subclass’s method must be able to be used the same way as the value returned by a superclass’s original method.

Creating code that abides by this principle isn’t simple, as the compiler can only verify that the structure of your code is correct, not that any specific behavior of your code always executes when desired. Creating robust test classes and cases is the best way to check if your code is following the Liskov substitution principle, checking portions of your program using all subclasses of a particular component to ensure there are no resulting errors or loss in performance.

The author of this article series has included another code example representing a coffee machine to illustrate this design principle, as they have done in their previous articles. They present the problem of creating different subclasses of coffee machines from a generic parent class, with two classes with an “addCoffee()” method that accepts two different types of objects, CoffeeBean and GroundCoffee. The author suggests two solutions, either creating a common Coffee class that can be utilized by both the BasicCoffeeMachine class and the PremiumCoffeeMachine class, or create a common implementation of a “brewCoffee()” method that also appears in both CoffeeMachine classes. The first solution would violate the Liskov substitution principle, because each CoffeeMachine class would need to validate that they are receiving the correct input type for the addCoffee() method, as the BasicCoffeeMachine class can only use the GroundCoffee type, and not the CoffeeBean type. The author’s preferred solution is to remove the addCoffee() method from both CoffeeMachine classes and implement a common brewCoffee() method in the CoffeeMachine interface implemented by BasicCoffeeMachine and PremiumCoffeeMachine. Since all CoffeeMachines are expected to brew filtered coffee grounds, this brewCoffee() method should have that responsibility.

I want to further understand this principle because in a past project, I made a simple video game that used lots of objects of different subtypes that inherited from a single GameObject parent class. If I want to return to that project, or begin a similar one in the future, I want to make sure that I am not losing any functionality as I design and implement subclasses of a broader parent class.

From the blog CS@Worcester – Michael's Programming Blog by mikesprogrammingblog and used with permission of the author. All other rights reserved by the author.

Week 8: CS-343

SOLID Design Principles

SOLID is a subcategory of five principles, the first being:

Single Responsibility Principle (SRP)

SRP states that a class, module, or function should only have a singular function.

For example, having a class ‘Animal’ with methods ‘displayAnimalName()’, ‘animalSound()’, ‘feedAnimal()’ would be a violation of SRP because each method has a different function. Creating three separate classes that would each represent a functionality resolves the violation. This entails new classes are created for DisplayName, Sound, and Feeding. Although this may lead to more code overall, having separate classes allows for better maintainability. Without SRP, making changes to one functionality may break another.

Open-Closed Principle (OCP)

OCP states that code should be open for extension, but closed for modification. Therefore the code’s behavior should be extended by adding more code rather than modifying the existing code.

Function ‘getArea()’ calculates the area of a shape using a switch statement where there are cases for a square and circle. To add another shape, the switch statement must be modified to add a case for the new shape, violating OCP. To satisfy OCP, one would create separate classes for each shape that implements the Area interface. Now to add a new shape, one would add a new class and implement the Area interface rather than modify the original switch statement.

Liskov Substitution Principle (LSP)

LSP states that child class objects must be substitutable with objects of the parent class without affecting the correctness of the code.

Imagine a parent class ‘Bird’ with method ‘fly()’, and child classes ‘Penguin’ and ‘Eagle’. Both inherit ‘fly()’ because ‘Penguin’ and ‘Eagle’ are child classes of ‘Bird’. Because penguins cannot fly, the ‘fly()’ method in ‘Penguin’ has been overridden to do nothing. Doing this would violate LSP because objects of ‘Penguin’ would not be substitutable for objects of ‘Bird’.

Interface Segregation Principle (ISP)

ISP states that users should not be forced to implement interfaces they will not use.

Imagine an interface ‘Worker’ that has methods ‘work()’ and ‘eatOfficeLunch()’. Classes ‘FieldWorker’ and ‘OfficeWorker’ both implement the ‘Worker’ interface. Because ‘FieldWorker’ does not eat in office, the method ‘eatOfficeLunch()’ is unnecessary. This violates ISP because ‘FieldWorker’ is forced to implement ‘eatOfficeLunch()’ although the method will not be used.

Dependency Inversion Principle (DIP)

DIP states that high-level modules should not depend on low-level modules, keeping that as separate as possible.

For example, there’s a project with high-level systems (backend logic), that relies on low-level modules (database access). However the project’s team wants to change the database from one type to a different. Because the high-level systems are specifically written and depend on the database type, they would no longer work once the database type changes. Having high-level systems be more abstract and able to be implemented in different ways is ideal.

Conclusion

The article used was chosen because it gave examples with code, making understanding easy. As someone who wishes to have a career in software development, knowing the best practices for designing maintainable code is crucial. I expect to apply these principles to all future projects.

Resources:

https://www.freecodecamp.org/news/solid-design-principles-in-software-development/

From the blog CS@Worcester – Zack's CS Blog by ztram1 and used with permission of the author. All other rights reserved by the author.

Golden Rules for APIs

This week I have decided to view a source regarding APIs since that has been our focus in class recently. I had come across an article written by Jordan Ambra, an experienced software engineer from Pennsylvania, describing a few “rules” to keep in mind to create the best API possible and better maintain its health in the long run.

Jordan starts with his first point being proper documentation, he states “ask [a developer] to follow a tutorial or build something really basic in about 15 minutes. If they can’t have a basic integration with your API in 15 minutes, you have more work to do.” It’s important to have proper documentation because without such, having an expansive and robust API is great but what isn’t great is if the implementation of said API is very limited caused by developer confusion due to the lack of documentation.

The next point Jordan describes is the importance of API stability and consistency. This is key in having APIs last in the long run. Jordan uses Facebook as an example to show that while an API’s health won’t make or break a company, most people don’t have the capital or userbase Facebook does so recreating an API is going to be more costly for one person. Keeping track of version numbers by incorporating a change log is a great way to document changes so users know how and why to upgrade.

Another practice is to maintain API flexibility, if an API is or over time becomes too rigid it can cause integration to be difficult. Not all platforms are consistent, Jordan states, “It’s good to have at least some degree of flexibility or tolerance with regard to your input and output constraints.” So by keeping the API flexible, it’ll allow for the widest implementation without a large margin of error.

A very important aspect of APIs to prioritize is security. With malicious actors always looking for new weaknesses and ways to hijack or manipulate things for their gain, it is more important than ever to implement security safeguards to act as a preventive measure to prevent people with the wrong intentions from harming others.

Finally, the last thing Jordan brings up is the importance of an API’s ease of adoption. If things are overly complex or not universal enough it can turn people away from wanting to interact with your API. If you’re not able to efficiently use your API then how is anyone else going to be able to?

After reading this article, I hope to now be better equipped to create healthier and longer-lasting API(s) among a majority of APIs that are not easy to use which can be due to one, a combination, or all of the factors above. Focusing and prioritizing the rules set by Jordan will help in the future to improve my work.

Article Link: https://www.toptal.com/api-developers/5-golden-rules-for-designing-a-great-web-api

From the blog CS@Worcester – Eli's Corner of the Internet by Eli and used with permission of the author. All other rights reserved by the author.

Anti-Patterns

So what even is an anti-pattern? Well, anti-patterns are solutions to ineffective problems that eventually cause more problems than they solve them. They are often used because they seem to work very well but the long-term consequences are not usually thought of. Anti-patterns are just like a regular pattern but instead of a solution, they give a superficial solution but really isn’t one at all. Programming anti-patterns are known for common mistakes, they can lead to problems like maintainability. The spaghetti code is a good example of anti-pattern, it’s a code with no structure. There are random files scattered into random directories, the whole thing is difficult to follow and is tangled together. This usually happens when someone isn’t carefully thinking out their program and how smoothly it should be running before actually coding. It makes it basically impossible to add any new functionality, no matter how many changes you try to make and try to understand those changes you will still end up with countless issues with your code. Another example of anti-patterns is the boat anchor. The boat anchor happens when someone leaves a piece of code in the codebase because they believe that they will need it later but doing this actually can keep your project from moving forward, can cause your coding time to slow down, and mess up your codebase.

Avoid anti-patterns with better system management. You can avoid anti-patterns in your code by being more consistent with your system. By reviewing your code, looking through and making sure you have no typos and grammatical errors. These code reviews help improve the quality of your code and make it easier to find better solutions for any common problems your’re having. Always have someone else take a look at your code in case you did miss something that you didn’t see it’s always helpful to have a set of fresh eyes look at your code so it can help you tighten up your code. The process of engaging in code refactoring can also help make adjustments that will strengthen your code’s structure and framework without impacting the user experience. Refactoring is very helpful in simplifying your code construction.  It will make it easier for someone to take a look at your code and understand how you put the code together and be able to add any new functionality to the code as well. Making the code a visual can help give you as well as others a better understanding of the information presented to them. It can help you show the workflows, and analyze and brainstorm for any new improvements.

https://www.lucidchart.com/blog/what-are-software-anti-patterns

https://www.baeldung.com/cs/anti-patterns

From the blog CS@Worcester – Kaylene Noel's Blog by Kaylene Noel and used with permission of the author. All other rights reserved by the author.

Navigating Software Pitfalls: Understanding Anti-Patterns

In the intricate world of software development, as developers strive to create elegant and efficient solutions, they encounter not only best practices but also pitfalls to avoid. These pitfalls, often referred to as anti-patterns, are common design and coding practices that may initially seem like solutions but ultimately lead to more problems than they solve. Understanding and recognizing these anti-patterns is a crucial part of becoming a seasoned software developer.

At its core, an anti-pattern is a recurring design or coding practice that appears to be helpful but is counterproductive in the long run. It’s like a mirage in the desert – promising relief but ultimately leaving you more parched. Anti-patterns often arise from misapplications of well-intentioned ideas or a lack of experience in software development.

One common anti-pattern is known as “Spaghetti Code,” where the codebase becomes entangled and challenging to maintain. While quick fixes and shortcuts may seem like a solution, they often lead to a tangled mess that hampers productivity.

Another notorious anti-pattern is “Cargo Cult Programming,” where developers mimic code without understanding its purpose. This blind imitation can lead to code that lacks context and may introduce errors.

By studying anti-patterns, developers can proactively identify and avoid these pitfalls. It’s akin to recognizing warning signs on the road and taking detours to smoother coding practices. In the ever-evolving landscape of software development, understanding what not to do is as important as knowing what to do. Recognizing anti-patterns empowers developers to make informed decisions and craft robust and maintainable software solutions.

References:

From the blog CS-343 – Hieu Tran Blog by Trung Hiếu and used with permission of the author. All other rights reserved by the author.

rest apis

REST stands for Representational State Transfer. This means that when a client requests a resource using a REST API, the server transfers back the current state of the resource in a standardized representation.

From the blog CS@Worcester – Andres Ovalles by ergonutt and used with permission of the author. All other rights reserved by the author.

Comparing REST and SOAP API

APIs are what allow different applications to communicate with one another, making the intricate web we have access to today possible. Because the present world basically runs on the web, it is important to understand how the API system operates.

REST and SOAP API are the two main standardized types of APIs, and while the two work towards the same goal, that is, application-to-application communication, they operate differently.

Simple Object Access Protocol or SOAP API was developed by Microsoft in the late 90s. It was designed to be highly extensible so that future created functionalities could be added depending on the task for the API would be used.

Because of this extensible design, SOAP needed to be highly standardized, which is both an advantage and disadvantage. On one side, services written using SOAP API are very easy to error handle. SOAP is able to implement a built-in error handling system that even gives code suggestions for automated solutions, making it especially useful for users who do not own the web service.

However, on the other hand, while SOAP is compatible with any programming language, it was designed to rely solely on XML for messaging services. Though much of the process of writing the XML file can be automated or at least heavily simplified by the IDE using the Web Services Description Language (WSDL), this tool can only be used by NET languages (such as C# or F#). Other languages like JavaScript have to write each XML structure separately for each task, something programmers would rather not do.

Because of the more complicated nature of SOAP API, programmers later developed a simpler API system called REST.

Representational State Transfer or REST API operates using URL instead of XML to make requests. These requests are made through four basic methods: PUT, POST, GET, DELETE.

REST API is simpler than SOAP because it has the ability to use smaller messaging formats. It is also easier and often cheaper to use as it does not require the expensive tools to interact with web services that SOAP often utilizes. REST is also often faster than SOAP because it does not require extensive processing and can utilize the cache system.

Overall, the general consensus is that both systems are good at what they do and have their advantages and disadvantages. Programmers select which API to used based on multiple factors such as the language used, the environment, and requirements for the system or project.

In terms of my personal experience with APIs, I partook in an internship last summer which involved using Slack API and Python bolt — which are based on JSON-RPC — to send commands from a Slack channel to a raspberry pi. It was a simple program, but it helped me learn more about how APIs work and their important role in facilitating application-to-application communication.

For the future, I hope to look more into XML files/structures and familiarize myself with HTTP to learn more about using other APIs like REST or SOAP.

Source: https://blog.postman.com/rest-api-examples/

From the blog Stories by Namson Nguyen on Medium by Namson Nguyen and used with permission of the author. All other rights reserved by the author.