ProgrammingPro #26: Squeaky Clean Code, Hexagonal Architecture, and Test Automation
Bite-sized actionable content, practical tutorials, and resources for programmers
“I think that any program is only as good as it is useful, so in that sense, the user base is the most important part, because a program without users is kind of missing the whole point. Computers and software are just tools. It doesn't matter how technically good a tool is, until you actually have somebody who uses it.”
– Linus Torvalds (2007), Q&A: Torvaldson Linux, Microsoft, software's future
Welcome to this week’s issue of our programmer focused newsletter.
Before I get into today’s news and resources, I have some exciting news to share. We are planning to launch PythonPro: a Python professional focused newsletter. If you love the idea and would like to subscribe, do take part in this week’s survey at the end of the newsletter and opt-in.
Now let’s get into today’s issue. We are going to start by talking about how game changing GitHub’s Graph is going to be, and look at how AI is going to enhance code reviewing and testing. Also, we have the 🎥 TypeScript Origins documentary which I highly recommend watching.
In our tutorials and learning resources this week, we are focusing a bit on clean code and architecture, and design patterns, along with a great bunch of other stuff.
Here are my top 5 picks:
🎯Hexagonal Architecture with Spring Boot – an implementation guide
🎥 Python Tkinter GUI Design Using ttkbootstrap - Complete Course
In keeping with today’s theme, we also have an exclusive excerpt for you from Clean Code in Python, Second Edition from the Packt community. So, scroll down and dive right in!
PS: If you want us to find a tutorial for you the next time and want to opt-in for our new PythonPro newsletter, take the survey! As a bonus you can download a free PDF of the React Projects, 2nd Edition eBook upon completion.
Stay awesome!
Divya Anne Selvaraj
Editor-in-Chief
🗞️News, 💡Opinions, and 🔎Analysis
GitHub’s Innovation Graph offers ongoing data about software development: The new platform offers quarterly data on Git activity, developers, organizations, repositories, programming languages, and more, dating back to 2020. It also offers data visualizations and ensures accessibility by allowing free data downloads under the CC0-1.0 license. This eliminates the need for third-party GitHub data providers.
Time to Take advantage of AI-augmented software testing?: By 2027, 80% of enterprises are expected to integrate AI-augmented testing tools, up from 10% in 2022, according to Gartner. These tools can streamline testing, reduce manual intervention, and align with automation and intelligent testing trends. However, human involvement remains crucial for effective AI utilization in testing.
TypeScript Origins: The Documentary: This well-made film explores the history and community behind TypeScript, featuring core contributors like Anders Hejlsberg and insights from companies like JetBrains, Xata, AG Grid, Deno, Visual Studio Code, and Bloomberg. Worth a watch whether you are part of the TypeScript wave or not.
AI-Enhanced CodeReviews: Improving Software Quality and Efficiency: How do AI-driven tools offer unparalleled speed, consistency, and error detection? While challenges exist, the benefits of integrating AI are undeniable and companies like Facebook, Google, and GitHub are already on board. Read to learn more about these real-world use cases and the benefits of using AI reviewers in your projects.
New software detects money laundering faster than ever before: Computer scientists at King's College London have developed a groundbreaking tool for rapidly detecting money laundering. It's also open source, capable of handling large datasets over extended periods, and has potential applications in optimizing marketing campaigns and improving retail data accuracy.
Best Low-Code Development Platforms: This comprehensive list covers some of the most highly rated low-code platforms along with their pros and cons, pricing, and use-cases. Unlike no-code platforms, these platforms emphasize code customization as a primary feature. Read if you are looking for a low code platform for rapid application development.
Windows Terminal Preview 1.19 released, Terminal brought up to version 1.18: Notable additions include Broadcast Input for sharing terminal pane contents, a code highlighting web search feature, unfocused acrylic support, Suggestions UI for command recommendations, and emoji support in Command Prompts, along with various usability improvements.
🎓 Tutorials and Guides🤓
Running Automation Tests at Scale Using Python: This guide covers what Selenium is, how to set it up with Python, and how to write automated test scripts. It also discusses using the pytest framework for parallel test execution and the advantages of cloud-based testing grids like LambdaTest. Read to learn about streamlining your testing processes and staying competitive in the job market.
Pandas Merge DataFrames explained with examples: This article delves into pandas merge() and DataFrame.merge() methods, which allow you to merge DataFrames, much like SQL joins, supporting various join types like inner, left, right, and outer. Read for quick examples demonstrating how to use pandas merge() for different types of merges.
Selenium Webdriver Python Tutorial for Web Automation: The tutorial includes a sample Python script for automating interactions with a web page, along with instructions for running scripts on Internet Explorer and Google Chrome. Read if you're looking to start automating web tasks.
The Builder Design Pattern: This pattern works by separating the object construction code into builders, each responsible for specific steps. You can also use different builders for various representations of the same product. Read to learn more about how the Builder pattern simplifies object creation in complex scenarios.
Hexagonal Architecture with Spring Boot – an implementation guide: This comprehensive guide covers various aspects of implementing hexagonal architecture in a Spring Boot application, including use cases, primary and secondary adapters, testing strategies, and more, making it a valuable resource. Read if you are looking to apply this architectural pattern in your projects.
Communication Design Patterns for Backend Development: This article talks about five essential communication design patterns for backend development. These patterns determine how different components of your application communicate, impacting performance. Read to be able to choose the pattern that fits your application's needs for efficiency and real-time responsiveness.
Inverted Indexes: A Scala Step-by-Step Implementation Guide: Inverted indexes are crucial for quick document retrieval, like in search engines. Read if you are interested in building document search systems using inverted indexes in Scala.
JavaScript Event Calendar for Resource Scheduling: Learn to build a versatile HTML5/JavaScript event calendar with PHP and ASP.NET Core backends. Read to learn how to get the most out of this powerful tool to manage events effectively.
Build reliable and secure C++ programs: This article explores key practices for building reliable and secure C++ programs, referencing NISTIR 8397 guidelines. The author advises against hardcoding secrets and suggests regular rotation. Read if you are a C++developer seeking to enhance code security.
Java Library Development: Get Started Quickly with java-library-template: This article introduces the 'java-library-template,' a comprehensive solution for Java library developers which simplifies the process of creating and maintaining Java libraries. Read for more details on the CI/CD pipeline, project structure, Maven Central publication, Javadoc generation, security scans, and more.
Monkey-Patching in Java: This article explores various monkey-patching techniques in Java, including the Proxy class, Java Agent instrumentation, AspectJ for AOP, and javac compiler plugins. Read to learn how these techniques can empower you to enhance Java code behavior on the fly.
🧠 Expert insights from the Packt Community📚
An exclusive excerpt from Chapter 2, Pythonic Code in the book Clean Code in Python, Second Edition by Mariano Anaya.
Caveats in Python
Besides understanding the main features of the language, being able to write idiomatic code is also about being aware of the potential problems of some idioms, and how to avoid them. In this section, we will explore common issues that might cause you long debugging sessions if they catch you off guard.
Most of the points discussed in this section are things to avoid entirely, and I will dare to say that there is almost no possible scenario that justifies the presence of the anti-pattern (or idiom, in this case). Therefore, if you find this on the code base you are working on, feel free to refactor it in the way that is suggested. If you find these traits while doing a code review, this is a clear indication that something needs to change.
Mutable default arguments
Simply put, don't use mutable objects as the default arguments of functions. If you use mutable objects as default arguments, you will get results that are not the expected ones.
Consider the following erroneous function definition:
def wrong_user_display(user_metadata:dict = {"name": "John", "age": 30}):
name = user_metadata.pop("name")
age = user_metadata.pop("age")
return f"{name} ({age})"
This has two problems, actually. Besides the default mutable argument, the body of the function is mutating a mutable object, and hence creating a side effect. But the main problem is the default argument for user_metadata.
This will actually only work the first time it is called without arguments. For the second time, we call it without explicitly passing something to user_metadata. It will fail with a KeyError, like so:
>>>wrong_user_display()
'John (30)'
>>>wrong_user_display({"name": "Jane", "age": 25})
'Jane (25)'
>>>wrong_user_display()
Traceback (most recentcall last):
File "<stdin>", line 1, in<module>
File ... in wrong_user_display
name = user_metadata.pop("name")
KeyError: 'name'
The explanation is simple—by assigning the dictionary with the default data to user_metadata on the definition of the function, this dictionary is actually created once and the user_metadata variable points to it. When the Python interpreter parses the file, it'll read the function, and find a statement in the signature that creates the dictionary and assigns it to the parameter. From that point on, the dictionary is created only once, and it's the same for the entire life of the program.
Then, the body of the function modifies this object, which remains alive in memory so long as the program is running. When we pass a value to it, this will take the place of the default argument we just created. When we don't want this object, it is called again, and it has been modified since the previous run; the next time we run it, will not contain the keys since they were removed on the previous call.
The fix is also simple—we need to use None as a default sentinel value and assign the default on the body of the function. Because each function has its own scope and life cycle, user_metadata will be assigned to the dictionary every time None appears:
defuser_display(user_metadata: dict = None):
user_metadata = user_metadata or{"name": "John", "age": 30}
name = user_metadata.pop("name")
age = user_metadata.pop("age")
return f"{name} ({age})"
Let's conclude the section by understanding the quirks of extending built-in types.
Extending built-in types
The correct way of extending built-in types such as lists, strings, and dictionaries is by means of the collections module.
If you create a class that directly extends dict, for example, you will obtain results that are probably not what you are expecting. The reason for this is that in CPython (a C optimization), the methods of the class don't call each other (as they should), so if you override one of them, this will not be reflected by the rest, resulting in unexpected outcomes. For example, you might want to override __getitem__, and then when you iterate the object with a for loop, you will notice that the logic you have put on that method is not applied.
This is all solved by using collections.UserDict, for example, which provides a transparent interface to actual dictionaries, and is more robust.
Let's say we want a list that was originally created from numbers to convert the values to strings, adding a prefix. The first approach might look like it solves the problem, but it is erroneous:
class BadList(list):
def __getitem__(self, index):
value = super().__getitem__(index)
if index % 2 == 0:
prefix = "even"
else:
prefix = "odd"
return f"[{prefix}] {value}"
At first sight, it looks like the object behaves as we want it to. But then, if we try to iterate it (after all, it is a list), we find that we don't get what we wanted:
>>> bl =BadList((0, 1, 2, 3, 4, 5))
>>> bl[0]
'[even] 0'
>>> bl[1]
'[odd] 1'
>>> "".join(bl)
Traceback (most recentcall last):
...
TypeError: sequence item0: expected str instance, int found
The join function will try to iterate (run a for loop over) the list but expects values of the string type. We would expect this to work because we modified the __getitem__ method so that it always returns a string. However, based on theresult, we can conclude that our modified version of __getitem__ is not being called.
This issue is actually an implementation detail of CPython, while in other platforms such as PyPy this doesn't happen ….
Regardless of this, we should write code that is portable and compatible with all implementations, so we will fix it by extending not from list, but UserList:
from collections importUserList
class GoodList(UserList):
def __getitem__(self, index):
value = super().__getitem__(index)
if index % 2 == 0:
prefix = "even"
else:
prefix = "odd"
return f"[{prefix}] {value}"
And nowthings look much better:
>>> gl =GoodList((0, 1, 2))
>>> gl[0]
'[even] 0'
>>> gl[1]
'[odd] 1'
>>> ";".join(gl)
'[even] 0; [odd] 1; [even]2'
Don't extend directly from dict; use collections.UserDict instead. For lists, usecollections.UserList, and for strings, use collections.UserString.
To get a more comprehensive preview of the book's contents, read the first part of Chapter 2 available for free and buy the book here or signup for a 7-day free trial to access the complete book and the entire Packt digital library. To explore more, click on the button below.
🔑 Secret Knowledge: Learning Resources🔬
A Practical Approach to SBOM in CI/CD Part II — Deploying Dependency-Track: This article provides step-by-step instructions for both local deployment using Docker Compose and Kubernetes deployment with Helm charts, in your environment. Read to gain in-depth knowledge of deploying OWASP Dependency-Track for managing SBOM and enhancing software supply chain security.
Deconstructing Go Type Parameters: Learn how to create functions like Clone that return the same type as their argument, ensuring type compatibility in your code. Read to unravel the complexity of generic functions and the need for precise type matching.
Comfy with Python basic tooling, now what?: Read this article to learn how you can elevate your Python game with advanced tools like ipython, pdb, black, and more to improve code quality, readability, and testing efficiency.
Virtual threads: Are futures a thing of the past?: Imagine creating a CompletableFuture to asynchronously produce a string of stars, where join() both synchronizes and retrieves the result. Read to grasp this evolution fully, simplify your code, and to refine your Java concurrency skills.
Addressing the underworld of software engineering – Fixing nasty code gremlins and whimsical architectures: Are you battling with code that resembles a Jackson Pollock painting, filled with tangled conditionals? Read to conquer coding chaos, learn how to tame spaghetti code, silence gossiping variables, and more.
Debugging Tips and Tricks: A Comprehensive Guide: This article covers essential debugging techniques like “Rubber Ducking” and “Flipping the Direction” to boost your debugging productivity. Read for practical tips and strategies to tackle software issues effectively and make debugging a more joyful experience.
7 Tips to Write Clean and Better Code in 2023: From using meaningful names for variables to following the Single Responsibility Principle (SRP), this article will help you to discover best practices for making your code readable and maintainable. Read to avoid unnecessary comments and format your code for readability.
How Long Should a Class Be — When Clean Code Isn’t Really Clean: Are strict class length limits really promoting clean code? While coding standards and language conventions affect class length, setting stringent limits can lead to bad practices like overly complex one-liners. Read this thought-provoking article for a nuanced perspective on clean code practices.
Python Tkinter GUI Design Using ttkbootstrap – Complete Course: This almost 3.5 hours free to access video-course will help you discover how ttkbootstrap makes styling and functionality enhancements a breeze, making your Tkinter apps modern and user-friendly. If you would prefer to read rather than watch, you can find a pretty good summary here.
🛠️ HackerHub: Tools from GitHub⚒️
The Algorithm – Javascript: a repository that offers implementations of various algorithms and data structures for educational purposes and programming skill development.
datahub: an open-source metadata platform for the modern data stack, offers features, GitHub repositories, and a thriving community.
astro: a modern web development tool promising powerful developer experience and lightweight output, offering easy installation, documentation, and more.
pyannote-audio: an open-source Python toolkit for speaker diarization based on PyTorch, promising optimal performance and tailored support for your needs.
PowerToys: a collection of utilities designed for power users to enhance their Windows experience, offering various productivity tools, installation methods, and a roadmap for ongoing improvements and features.
📢 If your company is interested in reaching an audience of developers, software engineers, and tech decision makers, you may want to advertise with us.
We have an entire range of newsletters with focused content for tech pros. Subscribe to the ones you find the most useful here. Complete ProgrammingPro archives can be found here.