Follow Through on Commitments

I’m still working on my writing style and how I share content. Part of the purpose of this blog is to present myself as a respectable engineer. But there are times when I shouldn’t sugar-coat my experiences, and instead should seek to share a valuable lesson at the expense of my own vanity. This is one of those times.

Not Who I Thought I Was

Last month I had a rude awakening when I got lambasted (well-deservingly) for failing to follow through on a commitment to a friend. I had actually done two out of the three things I had promised to do for him that Friday, but the one I failed to do was far more important for him. The Saturday morning after my self-imposed deadline, I awoke to read a text message from this friend knocking away my past, present and future: 

“…If you don’t keep small commitments, you won’t keep big commitments. If you’re not responsible in one area, you won’t be responsible in other areas…”

Yes, he was completely correct. No arguments there. I needed to take all of my commitments more seriously. There really was no problem in doing the thing today or the following, but I had given my word that it would get done on Friday. 

What I didn’t see coming (and shame on me for not recognizing it) was a similar conversation with my girlfriend. After telling her about my blunder and the sternness of my friend’s words, she fell completely silent. And her silence said everything to me. When you’ve been with someone for seven years, there are a lot of opportunities to make commitments, and equally many opportunities to break them. Seven years of failed commitments, large and small, came rushing back to me (and to her as well).

That silence took me on one of those symbolic journeys to the underworld: “Oh no….I’m not the person I thought I was. The people around me don’t take me seriously because my word doesn’t mean anything to them anymore.”

Photo by Ian Chen on Unsplash

The Receiving End

Almost as if God himself had ordained it, the day after this ego-bruising experience, I found myself on the other side of the commitment problem, the receiving end.

My soccer team was scheduled to play for 3rd place in our Sunday league. Morale was low because of a crushing 5-0 loss in our semifinal game, the week before, we had been favored to win. There was no communication that week and no proper headcount until I kicked things off the night before the game. 

Commitment to communicate: Many of my teammates failed to even respond to the call to arms. There is always an implicit commitment that gets extended to us to appropriately respond and communicate with others, especially when one is in a long term relationship or team commitment. We shouldn’t kid ourselves that no response is ever an appropriate response.

Commitment to show up: Of the players that committed to show up via text the night before, four of them didn’t make it. No text, no call, just didn’t show up. 

Commitment to play: Because we were lacking numbers approaching game time, the players who showed up weren’t getting ready because they no longer wanted to play. 

After scrambling to find some guest players at the field and urging my teammates to get their boots on, we played. And we played well for that first half all things considered, up 2-0 by halftime (coincidentally, the worst score in soccer). The wheels fell off the wagon in the second half and we lost 3-2. Some of our players quite literally stormed off the field without saying a word…or paying their referee fees

Commitment to pay: The players that showed up and played, no longer felt the need to pay referee fees. The very equivalent of shoplifting, my teammates had accepted the services of an organized soccer game and now were going to leave the league manager empty handed.

After being told that this had been all my fault: I handed in my jersey, paid the entirety of the team’s bill, and left the field teamless for the second season in a row. (The first time has a life lesson in and of itself.)

The Cost of Breaking Commitments

My ex-team immediately scheduled a game the following weekend (in a different league naturally). But they didn’t see what I had seen. They didn’t fix the commitment problem. 

They didn’t communicate, they didn’t get a head count, they showed up late, they lacked the numbers, and they got burned.

Being an observer in their group-chat, I’ve read painful messages from guys in the last few weeks that showed they had lost all confidence in their teammates. Some players would even text the morning of a game that they were just going to stay in bed because it didn’t seem worth it to go.

Some of us have known each other for ten years, and some more. But it only took a few weeks of broken commitments for the team to completely fall apart.

Commitment Problems Everywhere

When I learn a valuable life lesson, I often share it with my Toastmasters International club. I posed to them a simple question at the beginning of the meeting: “In 30 seconds or less, tell us about a promise that you plan on keeping in your life.”

HALF!!! Half of my club members didn’t answer the question. Half of my club members gave the same evasive response: “I try not to make any promises anymore.

The members of my Toastmasters club are not 20-something-year-old knuckleheads lacking proper judgment. They are retired accountants, leaders at universities, marketing directors, and well respected individuals in their families and communities. They are also members of Toastmasters International, where every week they voluntarily practice their ability to communicate with others and be leaders.

And HALF of them shared the same emotional response to my very simple question: “I’ve promised things in the past and burned myself too many times. It’s better that I promise nothing to anybody. That way I don’t disappoint anyone.”

Now it is time for my confession. My exact response to my friend Saturday morning upon reading his text was: “You are not the first [person I’ve hurt], and you won’t be the last. Though it’d be nice to think you could be. I just won’t make any more promises.” I have also said the same thing to my girlfriend on more than one occasion in our relationship.

The Stories We Tell Ourselves

What are we to do then? Honestly, I think it is actually very practical advice to cut back on what we promise, but only so that we have the resources and capacity to follow through on the promises we do keep.

The gut reaction that my club members and I experienced is a very Cain-like response. It’s one that originates out of resentment. Resentment that our good intentions in making the promise could have landed us in such a bad state. Resentment that the good deeds of our past are meaningless in the presence of this mistake. 

Cain, for all his efforts and labors, is not blessed with good fortune by God as his brother Able is. Instead of carrying his burdens with dignity and faith, he kills the ideal that he is called upon to embody.

Cain Slaying Able – Peter Paul Rubens (Public Domain)

We can choose the route of Cain, to live in resentment and kill our ideals. We can never make promises again to our friends and loved ones, thereby stopping the suffering. But it leaves us in a place of stagnation with those around us and with ourselves. It reinforces the story that we are the kind of person that does not follow through on commitments and that people should no longer count on us to help move life forward.

We should instead choose to work daily at how we can keep our promises. It might be a simple case of a needed organizational system, or it could be that we are plagued by escapism that chokes our time away from responsibilities. But the story should always be: 

“I am the kind of person that people can count on. I am the kind of person that can bear his responsibilities and those of others. I am the kind of person that can ‘transform chaos into order’ (Jordan Peterson).”

The more we practice this (yes, by making promises and following through on them), the more this story will become our realities.

Blessings Along the Journey

I would even go further to say that every struggle we face is a blessing because of the potential hiding just beyond our triumph over it. The dragon may breathe fire, but it also guards a treasure.

Two months ago, when I set out on my strength training journey, I saw the process entirely from the perspective that progress was synonymous with heavier weights. Lift more, get stronger, end of story. It’s certainly not a bad model for the world, but it is a very simplistic one. Two months of tire-meets-the-road training has reinforced the lesson that progress in a meaningful endeavor is rarely so linear or one-dimensional.

Struggles Along the Journey

The journey will quickly present unforeseen obstacles and challenges for us though. For myself in the gym, I learned that my body isn’t flexible or mobile enough to properly perform certain exercises:

  • My wrists and lats aren’t flexible enough for me to even get into the front squat position.
  • My rounded shoulders don’t let me put weight directly overhead for the military press and they feel incredibly uncomfortable in the back squat position.
  • My ankles and hamstrings both restrict my range of motion while performing a deep squat.

There were also long term body ailments that returned to the forefront of my attention. Some vertebrae in my neck and middle back have always given me trouble. For a while, I had let those problems go unnoticed. Now I’m forced to pay attention to them as I place heavy loads on my body and feel the weaknesses and discomfort first hand.

I wasn’t even prepared for the anxiety that set in each day on my drive to the gym. Apparently going into an unfamiliar place to face my own inadequacies, feel the silent judgment of strangers, and bruise my ego was a journey to the underworld.

Blessings in Disguise

Maybe the fact that there is so much unforeseeable struggle is a blessing in disguise. If we could clearly see the entirety of the hardship awaiting us before we even took our first step, would we still make the choice to step forward? Or would we hesitate and aim our lives elsewhere?

I would even go further to say that every struggle we face is a blessing because of the potential hiding just beyond our triumph over it. The dragon may breathe fire, but it also guards a treasure.

A Worthwhile Goal

My perspective on this journey has grown immensely. Instead of strength being simply about muscle size and output, strength now includes structural integrity, joint health, and functional ability. While I used to see stretching as a chore and avoid it entirely, flexibility and mobility exercises are now a part of my daily routine that I look forward to. My mindset changed when I learned that stretching wasn’t just a chore for after a workout, but instead a valuable tool that I could leverage to reach my goals: 

  • front squatting and olympic lifting
  • a long and enjoyable amateur soccer career
  • joint health and independence in old age

A worthwhile goal can completely change our perspective of our day-to-day actions.

“He who has a why to live can bear almost any how.”

-Friedrich Nietzche

Learn to be Braver

Even though my initial fears of going to the gym turned out to be almost entirely psychological, there is immense truth in these words:

“Taking on your fear as a voluntary challenge does not make you less afraid, it makes you braver.”

Jordan Peterson

I’ve been learning this for years at Toastmasters. Every Tuesday we volunteer to speak in front of our fellow club members. It feels like we become less afraid of speaking each and every week. And one day we find ourselves in a completely different environment on a Saturday, called upon to perform an anxiety provoking task. Our old selves would have run for the hills, but we step forward and voluntarily face the challenge because we’ve practiced being that kind of person.

A Different Story About Yourself

“Every action you take is a vote for the type of person you wish to become.”

-James Clear

The first few weeks in the gym were a psychological battle, and now it’s something I look forward to each day I go in. The story I tell myself has begun to shift; I am the kind of person who goes to the gym to improve and maintain his body. And this mindset is spilling over into other areas of my health: the food I eat, my posture at my desk, the time I go to sleep, and yes, even how regularly I floss (thank you Dan John).

More generally and maybe most importantly, I’m training myself to be the kind of person who can follow through on his commitments, have faith in the process, and see long term visions to fulfillment.

Sharing Code Between Firebase Hosting and Functions

One issue my team had for the Glamis Recovery project was sharing code and logic between our frontend (Firebase Hosting) and our backend (Firebase Functions). If you’re not familiar with firebase, Hosting and Functions have certain assumptions and requirements of your directory structure for deploying to these services:

  • Your frontend code will most likely be bundled and placed in a distribution folder for the Firebase CLI to see. Each page of your web app needs an html file with its Javascript dependencies linked to it.
  • All of the backend code needs to be in the functions directory to be deployed. The index.js file will export all of the functions for Firebase to deploy.

Depending on how you setup your project’s directory structure and devops processes, which new web developers and users of Firebase may find challenging (because I certainly did), you may run into the problem that it’s difficult to share code between Hosting and Functions.

This blog post is about the dangers of keeping your frontend and backend logic separate from one another and what to do about it to improve the maintainability of your software project.

Divergence of the Codebase

Our codebases diverged for the following reasons. Don’t let these happen to your project.

  • The frontend was using ES6 modules but the backend was using CommonJS modules. Though the Firebase Functions documentation features examples with CommonJS require statements, you can just as easily use ES import statements as well. VS Code will even give you a hint about it and do it for you.
  • Letting the two different directories be a “knowledge gap” for your codebase. There are ways around this that we’ll talk about below.
  • Poor adherence to software engineering principles. Focus on modularity, Single Responsibility, and testability from the beginning. Refactor your code when is out of compliance with these principles.

Keeping the Codebase DRY (Don’t Repeat Yourself)

Our codebase truly wasn’t very big, so we decided that we would just duplicate any logic that was needed for both the frontend and backend. Maybe that meant writing duplicate functions or maybe that meant copy-pasting entire directories of class files.

We thought, “if we change it in one place, we’ll just make sure to change it in the other.” BIG MISTAKE FOLKS. BIG MISTAKE.

This was just a two person team. Sure, maybe this would work out because we both knew everything about the codebase…at the time. Maybe we could keep each other accountable…for a while. Maybe we could be careful and diligent…until that one time.

Just think back to a time when you stepped away from a piece of code for a month. After coming back to it, it’s basically like we weren’t the original authors at all. Our brains are the epitome of an LRU Cache. And attempting to manage the continuous upkeep of this much duplicated code went south pretty fast.

It wasn’t maintainable. The code diverged. There were bugs.

Solutions that Didn’t Really Work

There were a couple of things that we tried that didn’t pan out as solutions. Keep in mind the following directory tree. This is how our project is generally structured. Most of the code we want shared originated in the src directory with all our frontend code.

project-root/
├─ functions/
│  ├─ node_modules/
│  ├─ .gitignore
│  ├─ index.js
│  ├─ package.json/
├─ node_modules/
├─ hosting_distr/
│  ├─ bundled_frontend_code
├─ package.json/
├─ src/
│  ├─ shared_resources/
│  ├─ frontend_code.js
Symlinks

In Linux, Symlinks are special files that exist in one directory and “point to” a file somewhere else. We thought we could keep all of our frontend code in the src directory and have symlinks in the functions directory pointing to the shared files in src. This works when you’re running the firebase emulators locally on your computer, but as soon as you got to deploy said functions directory, expect to get errors because ALL of the code needed for the functions needs to exist in that directory and the CLI can’t resolve the symlinks for you.

Local Node.js Modules

The Functions docs say that you can use local Node.js modules as part of your function. The local module is actually just another pointer, this time managed by npm instead of the OS; npm copies the files over to where they get used. You need to run npm install (from within the functions directory) to actually get these local modules from src copied put into the functions/node_modules directory.

This seemed promising, until you read the starred note: Note: The Firebase CLI ignores the local node_modules folder when deploying your function.” In essence, this means that Firebase will call npm install for you on their backend and if that local module isn’t inside of the functions directory when they do that, npm will be very unhappy.

Private Modules

What about taking local modules a step further and putting the shared code into a private package on the npm servers. That way Functions can have the code when it calls npm install.

For $7 a month to have a paid npm organization, be my guest. I pay $0.22 to Firebase each month for the entirety of my backend services. I wasn’t going to pay $7 to share code with myself.

Current Solution

Identify and Organize Shared Resources

Instead of actively maintaining two copies of the shared resources with man-power and version control, we keep the authoritative copy in the src directory and we deleted all duplicate code out of the functions directory. Anything that may need to be shared between the frontend and backend is organized in the shared_resources directory:

shared_resources/
├─ classes/
├─ constants/
├─ enums/
├─ utils/
Have Backend Code Import from Shared Directory

Let the backend code import from the shared directory as if that shared directory existed inside of the functions folder. I promise that it will be there when the code runs, even though I just said that we removed it all.

import Vehicle from "./shared/classes/Vehicle.js";


import PERMISSIONS from "./shared/enums/Permissions.js";
import { MIN_COVERAGE_LENGTH } 
from "./shared/constants/coverage.js";
Using npm scripts to Copy the Shared Directory

We identified only a few moments when we needed to copy the shared resources over to the the functions directory, and we automated that copy operation into our existing development and build practices.

  • Before we launch the firebase emulator suite, the shared directory should exist in the functions directory.
  • Before we deploy any code, we always do a build. The build step is really for the frontend code, but now we just get the backend up-to-date at the same time.

We use npm Pre Scripts to automatically do the copy operation for us so we don’t have to think about it at all. Here are the relevant scripts:

"scripts": {

    "watch": "webpack --watch --config ./webpack.dev.cjs",
    "prefirebase:emulators": "npm run copy-shared",
    "firebase:emulators": "firebase emulators:start",



    "prebuild:prod": "npm run copy-shared",
    "build:prod": "webpack --config ./webpack.prod.cjs",
    "copy-shared": "rm -rf ./functions/shared && cp -R ./src/shared ./functions/shared"
  },
Have Git Ignore the Duplicate Directory

Because we only want a single authoritative location for these shared resources, we have git ignore the functions/shared_resources directory. No reason to be worrying about including it into version control.

Result

All of these work together to make sure the functions directory has its own copy of the shared resources when its needed. In reality, that copy is ephemeral: it’s not being tracked by version control and it’s getting deleted and recopied frequently by our npm scripts.

We no longer have to worry about our frontend and backend diverging because there is only one authoritative copy.

Shortcomings and Future Work

Two things come to mind when I think about this setup.

First, the entire time I was writing this post, all I could think about was how we hadn’t tried just putting the authoritative shared directory into the functions directory instead of src. src isn’t needed by Firebase Hosting; we only care about the distribution folder that gets made after everything is bundled together. Webpack could certainly handle reaching into the functions directory for any dependencies. I think we discounted this idea because of how much more code there is for the frontend. There was almost a psychological barrier there for us to write code for the frontend that looked like this:

import { SharedClass } from "../functions/shared/class.js";

Maybe it’s worth trying out instead.

Deploying Functions

Second, and more importantly, depending on how/when you deploy your functions, they may be utilizing stale code instead of the newest source code. Every time you modify the shared directory, there is a chance that code your functions depend on has changed. This should trigger a new deployment of your functions.

With Firebase Functions, you have the ability to deploy single functions, categories of functions, or all of the functions. We leaned on deploying singles for a while, but we recently reorganized so that we deploy categories. But we’ve been doing so manually instead of automatically in a CI pipeline.

Our hubris is begging for another important lesson. There’s an implicit assumption here in our setup that:

  1. We can remember all of the dependencies inside of our functions code.
  2. We will remember to deploy those functions when the dependencies get updated.

I’ll be working on this problem soon.

References

  1. Feature image: Photo by Chester Ho on Unsplash

Testing Sorely Needed

I’m working on an implementation of the dgemm matrix multiply function for the BLAS interface [1] for a Parallel Computation course. While trying to introduce a feature to take advantage of the processor’s cache hierarchy, I ran into a reoccurring issue in my software development career. This time it bothered me more than usual.

In order to complete the feature, four components would need to be added or changed for the program to function correctly. For someone who isn’t habituated to test their code after incremental changes, four changes is basically nothing. “Watch me perform dependency inversion while thrashing every class in the codebase,” says the programmer who had it coming. [2] But for someone who now expects more from himself as a software developer, this was now uncomfortable.

“If I think this through, map it out on paper, and act cautiously, I can probably get it on my first try,” is what I use to say. I knew better this time, and I was correct. It didn’t work the first time.

The problem isn’t that it didn’t work the first time. The problem was that I had made too many changes at once and now it was impossible to pinpoint which piece was failing just by looking at the result of my Frankenstein-memory-access-matrix-multiply.

Three of the four components were mostly self-contained and should have been unit tested. These were also the most complex changes, so they DEFINITELY should have been unit tested. What were the two obstacles that prevented me from unit testing this code?

First, was inexperience. I should own up to my own shortcomings before criticizing the code of other people. Even though I’ve been using C/C++ longer than any other language, there’s a lot I still need to learn. Just in this codebase alone: make files, extern ‘C’ code, and static inline functions [3] all showed me how little I truly know. Furthermore, I’ve never properly tested C/C++ software before with unit and integration testing. So there was no way I was going to slap a testing framework into this codebase without first receiving many well deserved battle scars.

Second, was code structure. Two things seem almost inescapable in software.

  1. If you don’t start a program with the intention of testing, as the program grows, it will probably get too difficult to ever test.
  2. Even when you start out testing a codebase, as the project grows and becomes more complex, it will eventually also get too difficult to test.

This code is strictly academic, for learning purposes only. They thankfully included some handy debugging functionality to the project, but its creators didn’t imagine people would need to properly test this code. From the complexity of how the code is linked together to the static inline functions that make some important components impossible to access, this is not convenient to test.

I write this the day of my well learned lesson. In the coming weeks, I hope to learn a number of things about software testing in general, testing C/C++, and understanding some of the internals that have given me so much grief today.

References:

  1. http://www.netlib.org/blas/
  2. Yea, I’ve done that. Not proud of it. I got what I deserved.
  3. Static inline functions actually made a lot of sense in this context since we’re trying to optimize the code to be wicked fast. Why declare a C function as static inline?

That Which We Need the Most

Six years ago, coming out of high school, I was terrified of public speaking, and honestly not the most comfortable conversationalist. What I needed most, was a voice. But the process of acquiring that voice required facing the fear at the root of my problem. There was no way to skip the hurdle or get around the fear.

Tuesday nights have become a symbol of growth for me. Tuesday nights are when I attend Toastmaster International meetings to become a better communicator. Most first-time attendees are invited to participate in Table Topics, an opportunity to speak for two minutes on an impromptu subject. It would be interesting to get the real statistic on how often first timers decline the opportunity. I have a feeling it’s around thirty percent. After having done a single table topics speech though, I don’t think people decline to speak out of fear ever again. Having seen this process over and over, I developed a simple heuristic that I’ve proudly used in some critical moments in life, though not as often as I would have liked: “If an activity gives me anxiety, it probably means I should do it!”

Carl Jung said it better though:

“That which we need the most will be found where we least want to look.’

It’s beautiful, simple, powerful, and exactly what we need to hear. There’s no better angle, no plan B, no waiting it out, and no way around our fears. There’s only moving forward into the darkness [1].

If I truly adhered to this principle, there’s no telling how much better my life could be. My current plague has been with me since my undergraduate program, the fear to apply for jobs. Writing resumes, outlining my strengths and weaknesses, putting myself out there, reaching out to people, and envisioning what I want out of a career are all activities that have developed monstrous and terrifying characteristics. I tend to keep a safe distance from them.

Is what we fear what defines our actions though? Do we look to our fears and makes Not-To-Do lists each morning? We should probably be asking ourselves what we need out of life. Well a year from now I’ll need a job so that I can support myself and show my girlfriend that she hasn’t wasted the lasted six years of her life. But I don’t just need a job.

What do I need the most? The ability to charter the course of my career. This includes:

  • Being able to market myself honestly – my value and my experiences.
  • Searching for teams and projects that align with my skills, principles, and interests.
  • Reaching out to friends, acquaintances, and strangers to have open and direct conversations about the challenges and needs of a company.
  • Being okay with rejection.

Framed by this critical message, “That which we fear the most will be found where we least want to look”, there seems to only be two options for me:

  1. Waste another year of my life waiting for the fear to go away, only to be faced with the exact same dilemma.
  2. “Do the thing [to] have the power.” [1]

I think I know what I’m doing this weekend.

References

  1. Dr. Jordan Peterson: Knights of the Round Table
  2. Ralph Waldo Emerson

What is software engineering?

This was the prompt of a small assignment in my software engineering course. At first, I was skeptical of such a trivial assignment, but it actually captivated my attention and helped me reflect on an important work experience. What this question is really about is understanding the responsibilities and expectations of the professionals and their work in this industry. And that, is no trivial matter.

Though I’m not able to fully answer this question yet, what I have thus far lies in an experience I had in my short time at Northrop Grumman and in a textbook.

I had the opportunity to get to know about other projects at the Northrop facility. One of them was digitalization of the cockpit for the UH-60L Black Hawk helicopter (UH-60V).[2] Developing software that controls the flight of military aircraft requires adherence to strict guidelines and regulations, namely DO-178B. For this reason, the software developers on the UH-60V team don’t do any programming, at least not in the way computer science students instinctively think about programming. They use Model-Based (or Model-Driven) Development and the SCADE software suite. For users of SCADE, their models are their documentation (at least one form of it) and their code. SCADE generates code using the developed models and the code it generates is certified to be DO-178B Level A.[3] “The levels are defined in term of the potential consequence of an undetected error in the software certified at this level.” [4] And Level A means, if there is an error in the code, there is more than likely a “catastrophic” outcome.[4]

There are many industries that software is developed for, each with its own set of regulations that need to be followed, some more strict than others. Developing software to keep aircraft in the sky has some of the strictest regulations. “Software Engineering” in this sub-industry means almost zero hand-written code. I don’t think projects are prohibited from coding, but the hoops that must be jumped through to prove that the code is error free and meets DO-178B Level A standards makes it financially impractical to introduce coding. With SCADE and Model-Based Development, they can provide the level of quality that is expected by the customer and do so on budget.

As a kid straight out of school, this was foreign to me. It didn’t look anything like the software engineering I’d seen. But I got to know people on that team and they shipped great software.

In his book, Software Engineering: A Practitioner’s Approach, Roger Pressman describes the realities and challenges of software in the twenty-first century before defining software engineering. Below are the summaries of those challenges and their takeaways [1]:

  • Software is now in such high demand that there are many forces pushing and pulling the direction of a project. “We need to understand the problem before building a solution”.
  • The requirements demanded by customers becomes more and more complex each year. “Design is a pivotal activity”.
  • More people and organizations are relying on software and to a higher degree than ever before. “Software should exhibit high quality”.
  • Software projects can see long term growth in their user base and increased demands in their capabilities. “Software should be maintainable”.

Pressman began with the challenges we will face developing software to prevail upon us that software projects won’t succeed on accident. Software will need to be ENGINEERED.[1] He puts an emphasis on the verb engineering. Pressman uses the diagram below to highlight the importance of various aspects of software engineering.[1]

The foundation of software engineering, Pressman teaches, is a focus on quality. A focus on quality, dictates the processes, methods, and tools that we’ll use to reach that standard of quality. And what’s better, if our focus is building quality software, then over time we’ll iterate and develop better processes, methods, and tools than what we already have.

So why did the Black Hawk team’s version of software engineering look so different from my preconceived notions? Because all I saw were their processes, methods, and tools. And if I’m being totally honest, all I really saw were their tools. What I didn’t see was the massive part of the iceberg hidden underneath the surface of the water: their processes and definition of quality. If you start with a focus on quality, then everything else can be great software engineering.

References:

  1. “Software and Software Engineering.” Software Engineering: A Practitioner’s Approach, by Roger S. Pressman, 7th ed., McGraw Hill, 2010, pp. 12–16.
  2. “UH-60V Black Hawk Integrated Mission Equipment Package.” Northrop Grumman, www.northropgrumman.com/what-we-do/air/uh-60v-black-hawk-integrated-mission-equipment-package/.
  3. “SCADE Suite: Integrated Model-Based Design & Development Environment.” Ansys, www.ansys.com/products/embedded-software/ansys-scade-suite.
  4. “Airborne Software Certification Explained.” Open, www.open-do.org/about/software-certification-101/.

For All Our Lives

An excerpt from “The Old Man and the Sea” by Ernest Hemingway:

“Do you remember when [Dick Sisler] used to come to the terrace? I wanted to take him fishing but I was too timid to ask him. Then I asked you to ask him and you were too timid. I know. It was a great mistake. He might have gone with us. Then we would have that for all of our lives.’

I can’t count the times I let fear and timidity get the best of me. This hits all too close to heart.

This passage sprang to mind while writing an email to a friend. I did not need anything from him. I had only wanted to write to him and thank him for all that he gave me while working on a project together: hope, a new attitude, curiosity, and a stronger work ethic. I let the thought of writing to him linger for far too long…three months really. Only after finishing the email did I recognize what I could have lost had I not written to him.

While attempting to relive moments of my life through this message, all the missed opportunities were quick to surface. And it’s certainly a powerful lesson to learn in one’s youth, but focusing only on the missed opportunities doesn’t seem to do myself, or the message, justice.

I’m simply grateful that I get to have this conversation with myself each morning:

“Do you remember the days when we’d get to Chemistry lecture early and enter through the back hallway so we could get a seat in the front? There was always that girl I wanted to get to know, but I was too timid to say hello to her. Then I asked you to approach her and you did. I know. It was the greatest thing we ever did. She was amazing. And now, we have each other for all of our lives.’

First Real Software Testing Experience

In the spring of 2020, I participated in a graduate software engineering course focused on the software principles of modularity. We read many canonical articles and put their messages into practice by refactoring an Android mobile game. My team and I implemented very few automated tests in our test suite, mostly because we spent so much time attempting to refactor the system. But when we did make tests, they became outdated in a week’s time because of how rapidly we were making changes.

This summer, I set my sights on building a website for Toastmasters International using Python and Django. The tutorial I used to learn Django was Vitor Freitas’s. Vitor’s dedication to sharing knowledge with others is astounding and his Django tutorial is phenomenal. What surprised me most throughout the course was his emphasis on testing. Looking back, the tutorial was as much an intro to software testing as it was an intro to Django.

Once my project diverted from the tutorial material, most of my testing involved copying and pasting test classes to fit all my new views. Later, I learned that I could test whole models. And I was most proud of how I tested user permissions for some of the views.

Though there wasn’t much to my test suites, I had gotten far enough along to develop an eye for what could be improved in my system:

First, I was repeating myself a lot! I’m sure the Don’t Repeat Yourself (DRY) principle also applies to test suites. I attempted to utilize the “setup” method of the Django TestCase class as best I could, and even used some inheritance to make the concrete test classes less repetitive. Still, I couldn’t shake the feeling that there wasn’t an “authoritative and unambiguous representation” [1] for components of my test suite. The finest example was user permission tests. Instead of directly testing the mixins that I created by inheriting from UserPassesTestMixin, I repeatedly tested the views that used these permission mixins.

Second, the dependency chains created by my models made it more complex to test individual components. If every Club could have Meetings, and Meetings could have Performances, and Performances could have Evaluations, then I was creating Clubs, Meetings, and Performances simply to test the Evaluation components of my system. Surely, this problem lies in a failure of mine to properly include abstractions between my components and utilize Dependency Inversion. [2]

As my first real experience in software testing, the following are just a few of my big takeaways for why I’ll be taking testing more seriously in my projects from now on.

  1. I was much more confident to make changes when I knew I had tests backing me up. With each added test, I was less worried about making mistakes because my failed test cases were my safety harness and light in my tunnel.
  2. While learning C++, I remember relying on the compiler to be my test suite. This Django project has really taught me that you can’t rely on the compiler errors to know you’ve done something wrong. The Django template language and many components within Django rely on strings. On several occasions I had features that simply weren’t working and couldn’t believe that uninstantiated variables in these string types didn’t throw helpful errors. All the more reason for better test suites.
  3. I even got into the habit of using test cases that always failed as a reminder that I hadn’t yet implemented a certain feature. With tons of links on each webpage, it was incredibly easy to forget a hyperlink here and there. Testing then became a habit that was more congruent with the David Allen “Getting Things Done” mindset I’ve been trying to develop. I let my testing suite be my reminder of unfinished tasks so I could free up space in my head for the task at hand.

You can find more info about my Toastmasters Feedback project here.

References:

  1. Orthogonality and the DRY Principle, A Conversation with Andy Hunt and Dave Thomas, Part II by Bill Venners, March 2003.
  2. Robert C. Martin, “The Dependency Inversion Principle”, C++ Report, May 1996.

Manifesting change with pen and paper

An important reason why I started this blog was to present myself to future employers as a responsible authority. I fear part of this post could serve to cast doubt on my character and abilities. Regardless of perception, I would rather share a genuine experience of progress than portray a false identity.

A few weeks ago, I realized I was neglecting some of my responsibilities. Long term projects weren’t being chipped away at, and small tasks were slipping through the cracks. My mind was a terrible reminder system, and the weight of my failure to act loomed large in my psyche. I returned to David Allen’s book, “Getting Things Done“, and started practicing a simplified version for myself.

I dumped every responsibility, large and small, out of my head and spewed them onto a Word document. This was as cathartic as a solid six-mile run. The pressure in my head was relieved and I could breathe again.

In the process of creating my Next-Action List out of the items in my In-Basket, I found myself seriously doubting that some items were ever going to get done. These weren’t large daunting projects either. They were simple tasks that my mind grew to associate with pain and friction. The fact that I had repressed them for so long, made them all the more difficult to perform moving forward.

Less than a week of practicing this system though, writing down my tasks and dedicating time to them each day, I began completing the very tasks that had seemed unrealizable just a few days prior.

My epiphany: whatever I write down on paper, I can accomplish.  Given enough time and effort, I have the capacity to manifest any change in the world I seek to make.

This is a lesson I should have learned many years ago. It’s also an idea several thousand years in the making:

For assuredly, I say to you, whoever says to this mountain, ‘Be removed and be cast into the sea,’ and does not doubt in his heart, but believes that those things he says will be done, he will have whatever he says. Therefore, I say to you, whatever things you ask when you pray, believe that you receive them, and you will have them.

(Mark 11:23-24, New King James Version)

After more than a year of avoiding my blog, I wrote down that I would turn this experience into a shareable message. I completed it within three days.

Enhancing the Game for Everyone

A teammate of mine left our team to start his own team after the first season together in our town’s new adult soccer league. I don’t think there were any hard feelings between Cruz and anyone else on our team to cause or as a result of his departure. If he had left to join the team that beat us in the championship game, that would have been a different story; for there is something inherently dissolute about leaving to join the best team instead of working to improve the team one is with.

Quite the opposite can be said about Cruz though. Even though our team was well poised to have another great season and have another chance at a championship, he left to start his own team with some close friends of his. He jokingly admitted that we would be able to beat his team easily. On the pitch, most of us wouldn’t hesitate to say that winning is everything, and I’ll be conservative with my estimate of myself when I say that it’s only been in the last couple of years that I’ve begun to see “winning” from more than just the scoreboard. So, for a really competitive soccer player, like Cruz, to start his own team, fully anticipating to do worse in the standings, is fairly counter-intuitive at first glance.

I would argue though that Cruz’s act of starting a team was an enormous win for everyone in the league.

First, Cruz gave ten plus guys a new reason to get out of bed on a Sunday morning. That’s ten more individuals who are healthier, stronger, and happier with themselves from having played a game of soccer. For most of us, whom aged out of organized sports after high school, the world is a much brighter place with the adventure, competitiveness, and camaraderie that comes with playing sports.

Second, Cruz brought people back into our lives that we hadn’t seen in a long time. Old teammates and rivals resurfaced with the creation of his team. Not only was it great to reconnect with individuals from our past, but it was surreal to relive the experiences together that once meant so much to us.

And lastly, Cruz was obviously being very modest when he said we could beat his team easily. His team came out with some real intensity and tied our team 3-3. For everyone there on that rainy Sunday morning, they witnessed a majestic display of athleticism, skill, and perseverance. The league is now more competitive. Yes, it is now more difficult to win a championship, but it has never been more convenient to play amongst and learn from high caliber soccer players.

Instead of settling for the status quo, Cruz took a risk and enhanced the game for everyone.