Just When You Think You're Done

I feel like I wrote last week's post on Aseprite just yesterday -- time flies near the end of the semester! Since that post, I've made a lot of progress on my issue. For starters, I actually finished it! ...Well, most of it -- waiting on some reviews at the moment, among some other things, but the features I've added work.  You can check out my PR here.

Adding elements to the UI wasn't nearly as difficult as I thought it would be, but it still required a lot of thinking on my end... And a lot of sifting through code, too. Mostly for examples, but also to make sure that I was following the project's general style -- the folks working on Aseprite have a unique way of formatting things, and I wanted to make sure I adhered to that as best as I could.

Anyway, after a couple hours' worth of work, I managed to finish everything up on a Saturday afternoon. I committed my changes, pushed them, and... Promptly forgot about them for the weekend. Come Monday, though, I did take a gander to see if any changes had been requested. My code hadn't been reviewed by that point, but I noticed that it was failing one of the tests. That was when I discovered that Aseprite even had tests -- there's no mention of them in the documentation, and no instructions on how to run them. That made things a bit tricky on my end -- I didn't want to bombard my branch with a bunch of tiny commits in an attempt to fix whatever was failing. Squashing commits is definitely an option, sure, but I'd still prefer to avoid having a cluttered commit history in the first place. Less work for everyone all around, right?

The first step in fixing this issue was to look at what tests my code had failed. That step wasn't taken until later in the week, because I got sidetracked by several other projects. The end of the semester is wonderful like that. Looking back on it now, I do regret neglecting my Aseprite code -- mostly because it did come back to bite me. Hard.

Despite having a small community, Aseprite gets updated very regularly, to the point where it's not uncommon to see multiple commits to the master branch each day. This makes keeping my code up to date is a bit of a trick; just when I think I've gotten everything updated and even with Aseprite's master, something else changes...

...Which has made fixing my issue quite difficult.

Some of the most recent changes -- none of which touched the files I modified -- did a wonderful job of breaking my repository. A submodule was updated, a file changed, and suddenly my local copy of the project decided that it didn't want to work anymore. Getting the project to run took precedence over finding out what part of the code had failed the automated tests, because at this point, all of it was failing. How exciting!

I'm going to be perfectly honest in that I have no idea how I really ended up fixing this problem. I think I reinstalled Aseprite's dependencies about ten times during this process, and tried rebasing my branch onto the upstream master another ten times. Something never failed to go wrong -- a library was missing, CMake was throwing unintelligible errors, my command line was cluttered with all manner of issues and warnings and things that I couldn't wrap my head around. One of the more common problems had something to do with a file called thumbnail_generator.cpp. I didn't record the what the exact error was, but I tried to fix it by literally copy-pasting the contents of Aseprite's master's copy of the file into my copy. And then doing that for the .h file. And maybe some other files too.

Unsurprisingly, none of it worked (that wold be too easy).

Unsurprisingly, I was very frustrated.

My branch was supposedly even with and identical to the master branch on Aseprite, yet it still failed to run. So what was the issue? Why wouldn't the project compile? I had no idea where to even begin with finding the problem, let alone fixing it.

So I took a break. I cooked dinner, watched a couple dumb videos on the Internet that made me laugh. I played a video game with a few friends, and then started to work on another project. I gave myself time to unwind and forget about the problem, just for a little bit. When I felt like I could tackle it again, a few hours later, I went back to it with a clear head. I know everyone always talks about taking a break when you get stuck, I know it's probably common sense, but it's something that I've never really done.  I'm stubborn by nature and once I'm set on something, I don't like just walking away.  It feels like giving up -- and though I know that isn't the case, it's still a hard feeling to shake.

Still, stepping back was the best decision I'd made. I was so frustrated with Aseprite not compiling that I wasn't really taking the time to sit down and think things through. I just saw the long blocks of text, littered with ERROR and FAIL and COULDN'T, and I panicked. I thought, "I don't know this project well enough to understand what those mean! I don't know the dependencies well enough to know what CMake is telling me! How can I possibly fix this?!"

But here's the thing -- I could fix it.   I did fix it.  At the time, though, I was too busy freaking out to step back and distance myself from the situation. I didn't necessarily need to understand 100% of the errors -- just knowing a little bit was fine. From those errors, I learned that thumbnail_generator and its associated files were causing the crash. From doing a git status, I could see that one of the submodules -- laf had been modified.

And that was my starting point.

I started by looking at the Aseprite master branch to see if there were similar changes. The laf submodule had been modified earlier that day -- one file had been changed. I went into the directory on my machine and checked what had been done to my copies of the files. As it turns out, more than one had been changed. Now, I'm not 100% certain as to what did it, but I am 100% certain it was my fault. I have a feeling that these files were updated by one of the many git submodule update --init --recursive commands I made in a vain attempt to get things working. Between that and the 20+ rebases, resets, and who knows what else, I most definitely botched something up. Again, this is why stepping back from a problem is important: getting to wound up can lead to making a bigger mess of things, which is exactly what I did. I'm not good enough at Git to just thoughtlessly plug in commands -- I don't have the experience to know exactly what they'll do to my files. That's something I should have recognized sooner, and something I should have avoided by just leaving the issue alone for awhile and coming back to it later.

But hey, what's done is done, and regardless of what I'd done previously, I now had an idea of what I needed to do. It was by no means a solid idea -- quite frankly I didn't know if this would fix things or break them further -- but it was a start. I undid the changes to all but one of the files in the laf submodule -- the same file that had been updated earlier in the day. From there, I added the changes, and then went to recompile the program. If it failed, I assumed I'd have to go the opposite way -- save the changes to the files.

As it turned out, I didn't need to do that. Reverting the changed files back to their previous state was enough; I was able to compile and run Aseprite without a hitch. From there, I rebased the branch that I'd been working on my issue, getting it up to date too. ...Or at least, I tried to. I'm not entirely certain what I did here, but oh boy, did I ever break things.  I did one force push and suddenly my PR was closed... By me.

I'm still not entirely certain why this happened -- I suspect it's something to do with me overwriting my branch's changes and making it identical to Aseprite/master, so the PR decided it didn't need to exist anymore.  Which makes sense, but it also made me panic a bit in the moment -- I was very, very afraid that I'd lost my work.

Thankfully, Git is very kind to amateur users who need to brush up on their knowledge of commands.  My old commits -- and therefor my old work -- were still saved in my repository's commit history, much to my relief.  Still, this is definitely a sign that I need to dig a bit deeper into how to use Git.

What I did next to re-add my changes is probably further testament to that, too.  At this point I didn't want to touch any git commands that weren't a basic add/commit/push/pull, so I did something that was probably not very efficient.  I combed through the last commit on my repository from before I broke everything, and found all the changes I'd made.  From there, I just copied and pasted them into my local copies of the files.  Inefficient?  Absolutely.  Did I mess up?  Absolutely not.  Was I grateful that I only modified seven files, and not, say, twenty?  Yes, yes I was.

After I'd finally made my changes, I decided to try recompiling the project on my computer -- I figured there was no point in pushing to my repo if the project had some syntax errors.  Much to my surprise (and delight), everything compiled and worked flawlessly.  So I made my commits, pushed them, and like magic, that PR I had accidentally closed was re-opened and showing my most recent changes.

..All that, and I still haven't gotten my code to past the Travis CI test.

If you recall, that was the initial purpose of all this -- to try and fix my code so it passed some tests.  But one thing led to another, and I went from being "done" to "I'm never going to finish this" to "I can't believe I got this to work".  A normal day in software development, all in all.  On the bright side,  I've since managed to pinpoint the issue by looking at the details of the Travis CI build -- something to do with an assertion in ui_context.cpp not working.  I suspect the culprit is in context_bar.cpp, since that's the only UI-related file that I modified.  I don't know if I'll be able to fix things by the end of the day, but overall I'm still very satisfied with the work I've done.  This is the most significant contribution I've ever made to an open source project, and while this small amount of code may seem inconsequential to some, it marks a huge milestone for me.

I've learned a lot by working on Aseprite, and I'm really glad that I stepped outside of my comfort zone to work on it.  Going into fixing the issue, I actually didn't anticipate having to deal with UI elements -- I thought it would just be adding some code behind the scenes and boom, done.  When the owner came back with instructions to add buttons, my immediate reaction was "oh no".  But I'd already committed myself to fixing the issue, so I couldn't just up and back out.  Thanks to this, I was able to learn a lot -- the basics of UI in C++, for example, or how to set up CMake and Ninja and Skia (Aseprite has a lot of dependencies).  I also learned some pretty valuable debugging skills -- especially with respect to figuring out submodules.

Before I end this post, I want to reflect just a little bit on what I've written.  As I was typing this out, I kept wondering if I was admitting to making too many mistakes.  I kept wondering if what I wrote was going to reflect poorly on my skills as a developer.  Maybe it does.  Maybe it doesn't.  I decided that, regardless of what people may think of me, I still wanted to post the full, honest truth.  I spend a lot of time thinking I'm an idiot for making these dumb, small mistakes.  I spend a lot of time thinking "wow, all my classmates are so much better than me -- there's no way I'll make it as a programmer, not when I keep messing up these little things."

When all you see is incredible pieces of software and a massive codebase, and perfect PR after perfect PR though, it's easy to get caught up in this idea that you're the only software developer making dumb mistakes... Or making mistakes at all.

Which just isn't true.

I've seen pull request's flooded with a large number of commits because someone didn't rebase their code.  I've helped my classmates out on programming assignments.  I've watched several of my professors run code that doesn't work as intended, or just doesn't work at all.

Nobody's perfect.  We all have bad programming days, we all have days where we miss a semi-colon or mistype something or get our logic wrong.  Whether we're just starting out or 20+ years into our careers, we always trip over something.  All those "perfect" things we see are the end-products of countless mistakes and refactors and whatever else.  Making a mistake or not fully understanding something doesn't make you a bad developer -- it just means you need to read up on the material, fiddle with the code, and ask some questions.  Regardless of what happens in the end, you'll still walk away with the most important things: new knowledge, and more experience.

Maybe all of this is just common sense, and I'm preaching to the choir here.  But this is something I've personally struggled with for a long time, and if putting this out there helps anyone, then it's worth saying that...

You are not a failure for making a mistake. 

You are not less of a developer. 

You just have a little more to learn, and a little more work to do.

Comments