πŸ‘‹ welcome to

Phoebe Hong

's website 🏑

My favourite tasks as a product developer, ranked

2020-02-16

Over the years of working as a product developer, that is, a software engineer who works directly on software products, I have discovered that I can have a bad day or a good week depending on what kind of task I work on. Below is the list of tasks that excite me, compared to the ones that drag me down.

3. Fixing fun bugs πŸ›

Not all bugs are made equals. Some bugs are more fun to catch that others. These bugs usually are easy to reproduce, have moderate impact on users, and come from such an unexpected place that I am genuinely entertained by the detective work involved in the debugging process, and also learn another way software can be flawed. The fixes for these bugs improve the codebase.

The opposite are the bugs with seemingly random occurrence. Though they never are random, it can be hard to reproduce them and thus to debug. They may have too little impact that I am questioning the worth of debugging effort, or have such a critical impact that I am shitting myself. When the root cause is found, at times it is too obvious and I can't help but blame myself for not foreseeing them. Other times it is completely out of my control (i.e. caused by a browser bug reported 4 years ago, or our own legacy code that would take months for a proper fix). Their fixes may force me to write ugly patchy code.

2. Refactoring fun way 🧼

When you get to clean up the mess that has been annoying you for a long time, it's the best feeling in the world. Refactoring is more rewarding when it entails a creative solution and it is easy to verify that the new code does not introduce any regression. Some refactoring requires little effort compared to the massive problem that it's solving - this is golden!

Refactoring can be frustrating too, when the new code I'm introducing is nothing innovative and I can't quite shake out the feeling that it should never have ended up this way initially. Also when it's hard to test the change. Sometimes it's just tedious and repetitive as well.

1. Building new fun features 🌡

This one requires little explanation - building something new is exciting, especially when it isn't like any existing thing in the codebase, when there are many known unknowns and when it requires sophisticated design solution. Sometimes I have to learn a new piece of technology too. I also find it motivating when I know how much love the new feature will get when it's released.

Sadly, some new features are extremely predictable. I can come up with a boring implementation plan soon enough, and all I got to do is to stick to it. This doesn't necessarily mean that the feature is small, so it can go on for weeks.

(Note: When you end up in this situation what you should do is to pray that the next project will be fun. What you may be tempted to do but shouldn't do is to find a way to make the task fun. A mature developer accepts that boring code must be written!)


Complexity in software engineering

2019-09-22

A Philosophy of software design by John Ousterhout was a beautiful read and I mean to write a more comprehensive review one day. Here is one quick note I want to make after finishing the book, which is about the word complexity. It is a word I hear in software engineering under two different contexts with two different meanings and I have yet to see them discussed in parallel.

First complexity, mostly talked about in analysis of algorithms, is computational complexity. This complexity is perceived by computers and indicates how efficient it is for them to run an algorithm. An algorithm with higher computational complexity requires more resources (namely, time and space).

Second complexity, often talked about in software design as it is in the said book, is cognitive complexity. This complexity is perceived by humans and indicates how efficient it is for them to build a program. A program with higher cognitive complexity is harder to modify and easier to introduce bugs.

These are two separate, important concepts but I do want to complain how we don't talk as much about the latter. This is very true in the commercial software development world that I am in, and it doesn't seem too different in academia according to Ousterhout. Hence my reaction to many book reviews I found online saying "the wisdom this book has is basic, good for undergrads maybe" was lots of πŸ€”πŸ€”πŸ€”. But let me elaborate on it in a dedicated post.


What we learn when we review code

2019-07-28

"Is this code review?": A meme. A Netflix show Russian Doll, misrepresenting what code review is. Otherwise an excellent show.

As software developers, code review is an important part of our job. Our coworkers in different functions may exchange emails, spreadsheets or invoices. Do they? Well, actually I don't really know what they do. What I do know, however, is that we write code, read code and talk about code all the time. For many of us, the exchange of code is our primary way of communication, and it is mostly done through code review.

I always try to argue the importance of code review beyond the popular perspective of seeing it as a gatekeeping process, or when occasionally talked about in the context of knowledge sharing, as a way for more senior developers to educate their junior counterparts. These views, though valid in every way, assume that authors learn from the reviewers but not the other way around, failing to capture the collaborative and communicative nature of code reviews.

Yes, the reviewer might pick up critical mistakes that the author didn't see. Yes, authors who lack certain experience can always benefit from the eyes of the more knowledgeable.

But hey, code review is definitely a two-way street and reviewers learn a ton too! Here I present a list of some of the things we learn when we review code.

πŸ—’ We learn more about the codebase

In the short term, we learn about the bit of code we are reviewing. And we may be able to say, 'I can fix bugs in this part of the code next time the author is on leave'.

In the long term, we obtain broader view of the codebase. What are the prevalent patterns that are being used? What are the 3rd party libraries we are using? What are the common pain points? Where are the parts of code that can potentially be combined together? These are some of the questions that can be answered by those who often read code that they didn't write themselves.

πŸ‘©β€πŸ’» We learn how to code better

There are immediate wins when you go 'I didn't know you can do that! I will use it next time'. Then there are yet again broader benefits from being exposed to different approaches and having discussions about them. You can have discussions elsewhere too, but code review is a good place to start them. Review comments that lead to learning often look like this: 'XXX is used here but we can achieve the same with YYY. What do you think are the reasons why we should prefer one to the other?'

πŸ“š We learn how to read code better

We get better at reading code as we read more code, and everyone knows that reading code - reasoning author's intent accurately and promptly - is an important skill.

🀝 We learn about our colleagues

We notice their styles, strengths, weaknesses, growth and so on. People management may not be your main gig, but these are definitely nice-to-knows. You'll know who to talk to when you have certain issues, when to give kudos for great contribution, and what relevant feedbacks to give to your peers.

πŸ—£ We learn how to communicate better

An amazing side effect you get from communicating your feedbacks about someone else's work all the time is that you get better and better at sharing your opinions respectfully and effectively. It takes real hard work, but, eventually, we get better at things that we put our time and effort into.


A simple case for Do Repeat Yourself

2019-07-04

The other day, I wrote a piece of code that went like this:

{panelType === "A" && <PanelA someProp={someProp} />}
{panelType === "B" && <PanelB someProp={someProp} />}
{panelType === "C" && <PanelC someProp={someProp} />}

And it was just ✨liberating✨

"Don't repeat yourself" was one of the first software development principles I've ever learned. (Who didn't?) And it took me considerable time and lots of frustration at prematurely abstracted code until I unlearned the dogma. The day I rage-googled and stumbled upon duplication is far cheaper than the wrong abstraction, I was born again as an appreciator of the beauty of simplicity and straightforwardness.

Say, for some reason, I "refactored" the code to look like this:

const panelTypeComponentMap = {
  "A": PanelA,
  "B": PanelB,
  "C": PanelC,
};

const PanelComponent = panelTypeComponentMap[panelType];
return <PanelComponent someProp={someProp} />

❌ Things the "refactor" didn't do:

  • It didn't make the code less bug-prone

Because the new code looks more organized, it might give you (false) sense of security. Still, any mistake you can make in the previous code (i.e. omitting a panel type), can be made in the latter code in the same manner.

βœ… Things the "refactor" did do:

  • It made it harder to change the code

It doesn't particularly reduce the amount of effort needed to do something as simple as adding PanelD or removing PanelB. In a case a future design change requires passing different props to different panels, however, you can imagine how much more configuration we have to add in the latter code, whereas in the former, to put it in a very technically accurate term, it'd be super easy.

  • It made the code longer and more complex

More lines of code means more work. I had to use my brain extra hard to come up with pretty much meaningless variable names like panelTypeComponentMap and PanelComponent. Imagine the toll on the future readers of the code, learning more stuff, jumping around the code because most likely panelTypeComponentMap is defined on the top of the file or, even worse, in a different file.

More lines of code is bad, and to justify it, there has to be real good explicit benefit to cancel out its badness.

This is a very simple example, but the whole point is to spare writer and reader's effort with something small like this so that they can focus on more important matter. Principle here is simple. Don't fix a problem that does not exist (yet). Write naive code, your many if statements will do just fine. You can fix it later when it becomes a real problem.