From QA Engineer to Software Engineer: my journey

Posted by on August 22, 2024

At the start of the year, I was a Student Quality Assurance Engineer, testing educational games at an educational technology company. Fast forward to now, and I’m navigating the complexities of accounting software as an engineering intern. How did I make this change? Why did I choose this path? And what insights can I share with others considering a similar leap?

The starting point

During my third year studying computer science, I worked as a Student Quality Assurance Engineer at a local tech company testing educational games designed to make learning fun for children. The role gave me valuable experience and provided me with a solid foundation in QA testing. However, I was eager to take the next step in my career.

After Christmas, I began searching for opportunities that would take my skills to the next level. That’s when I came across FreeAgent, a local Edinburgh firm specialising in accounting software for small businesses and freelancers. I applied, landed the internship and now here I am!

Bridging QA and development

Transitioning from QA in educational games to engineering in accounting software seemed like a huge leap, but I soon found these two roles had plenty in common. 

QA taught me the importance of paying attention to details, which is a crucial skill in development where precise code is key. Catching bugs early, understanding edge cases, and ensuring that my code was robust were all essential lessons I carried forward. For instance, while creating a new page I initially overlooked disabling the continue button while awaiting a server response, leading to potential errors. Finding this while I was testing my work, I decided to disable the button until the response was received, ensuring a smoother experience for the end user.

Familiarity with testing frameworks like RSpec and Jest gave me a head start. As well as understanding how to write effective tests, this knowledge also helped me write code that was easy to test. Seeing all the bugs in the first drafts of my code was a humbling experience and it gave me a valuable opportunity for improvement.

My experience in QA provided a solid foundation in the software development lifecycle from planning to deployment. As a QA Engineer, I created test plans, designed test cases and planned the testing phase of development cycles. But now, as a developer, my responsibilities also include implementation and maintenance and ensuring the code works as intended through continuous testing and necessary updates. This is especially important at FreeAgent, where there are no dedicated QA teams. All the developers share responsibility in testing each other’s work, otherwise known as pre-production testing.

Learning and adapting

From my previous experience as a QA Engineer, I quickly found common ground in the way we work here at FreeAgent. I was already used to working in an agile environment, but joining the banking integrations team at FreeAgent was different. This team alone was almost as large as my entire previous company’s engineering department. It was quite daunting to be surrounded by so many people! Much like the bug-tracking systems I used in QA, FreeAgent uses Notion, an ‘all-in-one workspace’, to keep track of issues, sprints, tickets and just about everything else. GitHub was also a shared tool between the QA and software engineering experiences. Having these connections helped to smooth the transition, since I was already familiar with how to organise my workflow using the tools available.

As I developed within my role at FreeAgent, I quickly realised there were many new skills to learn as well as old ones to expand on. I’ve become (debatably!) proficient in Ruby / Ruby on Rails. Working on a real-world application has expanded my coding skills and deepened my understanding of web development. Additionally, I’ve gained more experience in JavaScript with Stimulus, making my apps not just functional but also interactive. 

Learning more advanced Git features – such as rebasing – has significantly improved my workflow by allowing for cleaner commit histories and better team collaboration. Nobody wants to scroll through a commit history that looks like a novel, especially when it seems like 1,000 files have been changed for no reason! Even though it takes a little bit more time to set up, the benefits of a tidy commit history are well worth the effort.

Pair programming is highly encouraged at FreeAgent, and rightly so, it’s a fantastic way to share knowledge, solve problems collaboratively, and improve your own code quality. Pairing sessions here have been invaluable in helping me learn and adapt quickly (thanks, Lea!).

Advice for aspiring developers

If you’re a QA Engineer looking to transition into a development role, here are some tips to help you on your journey:

👨‍💻Learn to code: Start by learning the basics of programming. Online courses, coding bootcamps, and university courses are undeniably useful, but the real magic happens when you start building things yourself. Dive into personal projects, no matter how small. Think of something in your daily life you’d like to solve. Maybe there’s already a Software as a Service (SaaS) equivalent and you can try to make it yourself!

🔍 Understand testing: As a QA Engineer, you probably have a solid foundation in testing. Use this knowledge to write testable code. Applying testing concepts in code is different from thinking about them abstractly. Use your QA experience to anticipate potential issues and design your code to be robust and reliable against anything that might be thrown at it!

🌐 Get comfortable with version control: Understanding version control is crucial for collaborative development. A solid basis in Git is essential, regardless of the platform you use. Version control is not just about saving your work, but also about collaborating effectively with others, tracking changes and managing different versions of your code.

🧩 Practice problem-solving: I love problem-solving at university, but I love solving real-world problems even more. Work on coding challenges and projects to improve your problem-solving skills. Real-world projects will help you understand how to apply theoretical knowledge in practical situations.

🌟 Embrace setbacks and stay resilient: Things won’t always go as planned, and setbacks are inevitable. I faced redundancy between applying for this internship and working at my previous QA job. Despite the challenges, I made it through and found an opportunity that excites me. Remember that resilience is key, so keep pushing forward, learn from your experiences and trust that you’ll always make it through.

Reflections and aspirations

Looking back on my journey from a QA Engineer to a Software Engineer, it’s amazing to see how much I’ve grown. My QA background provided a solid foundation in testing and quality, which has been invaluable in my software engineering role. Tackling real-world problems and devising solutions has been incredibly rewarding, and I look forward to continuing my career in software development, embracing new challenges as they come.

And just to make sure my features still work perfectly after I leave, I’ll be signing up to FreeAgent myself. Just kidding! (Not really!)

Blasting away with Jetpack Compose: Android development at FreeAgent

Posted by on August 19, 2024

FreeAgent’s talented mobile app engineers work within their own specialised technology stack. Unlike the developers for the desktop version of FreeAgent, we don’t use HTML, JavaScript, or Ruby. Instead, we work with Kotlin for Android development, and Swift for iOS. The FreeAgent mobile app developers work to keep feature parity with the desktop version, whilst juggling unique mobile-specific considerations. These considerations are vital to empowering mobile customers and their businesses. As such, our apps go through constant improvements; developers aren’t just making improvements to the code itself, but also how the code is written in the first place. Transitioning from the old Extensible Markup Language (XML) approach towards Android’s Jetpack Compose is one of the most impactful upgrades you can make to your Android development. During my summer internship, I worked with the Android developers where my user interface (UI) coding skills were completely revamped by mastering Jetpack Compose!

The OG – XML

Before I started my FreeAgent internship, I already had some Android development experience under my belt. This was by no means a thorough foundation, but more of a DIY, self-taught introduction to Kotlin and using XML for UI design. This crude foundation in Kotlin did thankfully relieve some of my pre-internship nerves.

In my previous experience with Android development, I was using Android’s XML vocabulary to design Android UI pages in the same way as you would design regular web pages. XML is very human readable and has been the established choice for Android developers for many years. As a result, some developers are just far more comfortable writing in XML, and the comfort of the team may come as a top priority in making your development practices successful.

While XML layouts excel in separating functional logic from presentation, there are some unfortunate drawbacks. XML adopts an imperative approach where layouts are static, meaning there’s a greater degree of control over fine details, but there won’t be a clean way to reuse components you’ve already built. Building up a library of reusable common components not only improves the efficiency of writing code, but also reduces opportunity for errors. The quirks of an imperative approach are only exacerbated when working with complex projects because of the sheer volume of XML code. XML code can be much too verbose particularly when you’re trying to build both a stylish and functional UI layout. These are issues our Android developers are currently phasing out in favour of a better approach with Jetpack Compose.

Static XML code to display “Hello World!”.
Jetpack Compose alternative to display the same “Hello World!” text.

The new(-ish) kid on the block

Android’s Jetpack Compose is a new(-ish, stable release debuted 2021) toolkit for constructing native UI. I started my internship with no Jetpack Compose experience and so learned it on the job. Luckily for me, there isn’t much of a change in thinking at all. Instead of writing in XML, we just write in Kotlin! Jetpack Compose is a declarative Application Programming Interface (API) where we focus on what we want the UI to look like, rather than how we are going to get there. Jetpack Compose can then condense numerous XML files into a single Kotlin file. By taking advantage of Kotlin syntax and not having to deal with XML files, our code is more succinct and easier to maintain.

Compose has the flexibility to use small stateless components that aren’t tied to any specific fragment, where states are passed directly into the composable itself. This separation allows us to reuse composables across entire projects and potentially across platforms. I can’t overstate how much I love these reusable and reactive composables!

But perhaps the most useful feature is that Compose and XML can be used in tandem. This is great for older XML projects because you can safely integrate composables without having to overhaul the entire thing. While the process of transitioning a fully XML project into Compose may take some time, it is fully doable with a gradual approach. This compatibility should remove any worries you may have about first learning Compose. It’s super easy to play around with smaller sections of your app and see how they could be created with Jetpack Compose.

Example of a composable preview.
Example of the composable (hard logic excluded) that makes the above preview.

Give it a go

Learning the tricks of Jetpack Compose has done wonders for my Android development. I find it much easier and more satisfying to create UI now. Spending this time with Kotlin and Jetpack Compose has given me greater insight to modern app design and a genuine curiosity towards learning how some of my favourite apps work. If you are even mildly interested in Android development, jump right into Android’s Jetpack Compose. My personal journey learning Compose has been a joy under the direction of the experienced FreeAgent engineers. Fortunately there are tons of super helpful tutorials online for even the most “simple” of UI setups. There’s no reason not to give it a go!

Hybrid working: finding balance in flexibility

Posted by on August 8, 2024

Recent years have shown that we no longer have to be chained to our offices. Particularly for those who do everything on a computer, remote working has brought welcome changes, and many people have adapted well to this new environment. Adopting hybrid arrangements, people can get the best of both worlds, and FreeAgent is an excellent showcase of workplace flexibility. Coming into my FreeAgent internship as a student accustomed to the online capabilities recently introduced at universities, I had some idea of what to expect.

University vs FreeAgent

Unfortunately, my well-practised university system of sleeping through sociable working hours and catching up online under the cover of night didn’t prove to be appropriate to FreeAgent. That said, hours are quite flexible, particularly when remote.

Interestingly, though I generally feel more efficient at home for university work, during my internship I’ve found myself operating at my best in the office. The FreeAgent office is a structured, neat environment in which everyone around you is contributing to the same product. There’s a collective goal and it’s easier to stay focused, not to mention it’s harder to get distracted by my phone for several hours!

Conversely, as I’m sure any fellow students can agree, other students can be very distracting. Sequestering yourself in the library is often the best way to avoid interruptions like a surprise encounter with someone you don’t quite remember, or perhaps a robotics project trundling past. The library works great for some, though it can lack that sense of community direction and motivation. Of course the real kicker has to be a crushing absence of tea/coffee-making facilities. If you’re anything like me, convenience and familiarity in a working environment are indispensable.

Why not always be in the office?

You may be wondering at this point: if the office environment is so great, why would I ever work from home? It can’t be denied that FreeAgent provides enviable perks to entice employees into the office. Free drinks, fruit, Friday lunches, and – most importantly – free biscuits. Well, working remotely has its own perks.

First and foremost has to be the commute. Even for a fairly short journey, the difference between travelling and rolling out of bed directly into your office chair can feel large, especially if you happen to be a night owl. The temptation of that little bit of extra sleep might just keep you remote for the day…

Another benefit is convenience. Make drinks from your personal stash of extremely specific coffee. Stockpile whichever snacks you prefer. Eat what you want at lunch instead of preparing it in advance or buying it on the fly. Even small conveniences like being available whenever a delivery is due to arrive can make working from home helpful. The environment is familiar, and can be set up however you like. Clearly both locations come with their individual benefits and there are some tradeoffs. But there are also distinct advantages to having both.

Balance

Quite simply, the hybrid approach provides variety. We’ve all found work monotonous at times and have probably welcomed breaks from the usual workflow. The solution? Split time between two different sets of surroundings and routines.

Simultaneously, you can leverage hybrid working to improve your work-life balance. Feel like you’re spending too much time at the office instead of your own space? Or maybe working remotely is impacting the separation between your job and your personal life? Everyone has different preferences, so adjusting the balance of days spent working in person and at home can allow individuals to tailor work to fit them. This is why it’s brilliant that FreeAgent has flexible arrangements for interns – why not change your plans for the day based on how you feel that morning? The freedom to choose makes me feel a lot more positive about the next day.


Another thing to consider is the type of work you might do best in different places. For example, if I’m fleshing out some complicated implementation details and need to put in some deep, uninterrupted thought – home is the place to be. However, set me to anything requirements or design-related and I need other people to discuss the problem with. In this case, hybrid allows you to have your cake and eat it! With fixed in-office days for your team, it’s easy to find a moment to chat to a designer. This brings us to one of the most important considerations for any job…

Social Aspect

Colleagues can absolutely make or break your time at work. Thankfully, the culture at FreeAgent is really friendly, and the staff make the office a welcoming place to be. But it can become exhausting being around other people all day, regardless of how nice they are. Getting that personal time that comes with working from home can prevent your social battery from behaving like that of my work laptop running RubyMine, whilst time in the office serves to keep up real human interaction and prevent feelings of isolation. Again, there’s a healthy balance here for everyone to find.

Now, it’s important not to understate the power of chatting. If you’ve worked remotely before, you’ll probably agree that online conversation just isn’t quite the same, whether that’s due to cutting out in a video call or slowly (and maybe more formally) messaging. It’s not just the social aspect of off-topic office banter – chat can be a powerful tool for making progress. Got a question about your project? A minor decision you’re deliberating on? Maybe you’ve forgotten the workflow for a database migration for the third time this week? Your teammates are right there to ask.

Fundamentally, ‘watercooler’ conversations just can’t be replicated online. After all, the concept is of a chance meeting and spontaneous chat, but remote workers are all confined to their own spaces. Setting up scheduled meetings for this specific purpose misses the point: in-person interaction does so much more to connect you to your team. On top of this, the off-the-cuff nature of a watercooler meeting makes it great for creativity. Just share what’s on your mind and get another opinion, maybe even create a plan. Often the solution to a problem will be found away from your desk during a coordinated tactical raid on the biscuit cupboard with a team member.

Hybrid FTW

If you hadn’t guessed by now, I’m a big fan of the hybrid system. The whole point is flexibility, so make it what you want. Find your own balance, maintain variety, and get to know your colleagues without socially burning out. Solve problems collaboratively to consider ideas you might not have come up with. Then take a deep dive into the specifics, focused in your own space. What to expect from hybrid working? It’s up to you!

It takes a village to raise an intern

Posted by on

Let me tell you about my worst day at FreeAgent.

At the start of June, I was eager to get started as the newest workflow intern, the team responsible for user-facing admin features. The first couple of weeks were spent bouncing between inductions and tech issues, but I finally found my feet at the beginning of week 3. Ruby was starting to make sense, my tech was up and running, and I’d wrapped my head around the project I’d be working on for the next three months.

The first few features were easy enough to implement. At FreeAgent, we split our work into tickets: small chunks of work that keep the development process swift and flexible. My confidence was solidifying after a few runs through code review and testing. I could actually do this! As week 4 passed, I started to feel properly independent, I had picked up the pace, and everything was full steam ahead.

So of course, week 5 was when everything went wrong.

T-minus 10 minutes to disaster

The first serious ticket in my project was a database migration, to add two new tables required for my project. Migrations are the solution Ruby on Rails uses to update the database structure. With a huge database shared between over a hundred engineers, proper migrations are essential for keeping everything synced up and running smoothly. During week 4, I had written the necessary migration code, tested it on my local environment, and sent it over to Ops, the team responsible for running migrations on the central server.

On Tuesday, I got a ping from Ops. My migrations had been run and everything was working fine. I had the go-ahead to merge my code into the main branch of the FreeAgent repository.

This code had already presented several issues. The previous week, I’d had to update it several times after realising the database tables contained fields I didn’t need. On Monday, I’d accidentally imported 200 commits and tagged every single team in the company for review. That misstep had taken an hour-long call with a colleague to fix. I’d been staring at this code for over a week, desperately wishing for it to be finished.

You have a merge conflict

They’re the words every software engineer dreads. Most of the time Git feels like magic, but merge conflicts turn it into a curse. I was not to be deterred. I clicked through to GitHub’s conflict resolution page. The cause was obvious: somebody else had run a migration after mine had been written, so there were edits to the database file I was missing. I added the appropriate fixes, and clicked submit.

Git cheerfully informed me that something had gone wrong, and I should try resolving the issue via the command line. I opened VSCode and re-applied the same fixes, then pushed to the remote. This solved one issue and immediately created another: my commit history now had two extra commits, and I didn’t want to pollute the central repo’s history. I attempted to squash my commits together. It failed.

This time, Git didn’t even have the decency to explain what I’d done wrong. It did, however, inform me that my merge conflict had somehow rematerialised. After two days of wrestling with uncooperative branches, I was on my last straw. I was tired, frustrated, sick of looking at the code and completely out of ideas.

Help arrives

After a deep breath, I sent out a call for help. First to respond was my assigned buddy; after trying and failing to resolve the issues, they pulled in Chris, another engineer on the team with more Git knowledge. Together, we spent an hour squashing one bizarre issue after another until finally, the merge conflict was gone for good. That just left a veritable swarm of ugly fixes that needed to be dealt with.

I worked on cleaning up solo for another hour or so, then ran into the same problem as before: I reached a commit that just refused to be squashed. By this point, Chris was in a meeting, but my deskmate Simon (who’d been watching me suffer through this all day) leaned over and asked if I needed a hand. Together, we managed to get the last dregs swept up – but in the process we wiped away the review approval I needed to actually merge the code. Josh, the reviewer who had okayed my branches in the first place, promptly jumped over from the other side of our desk bank to re-approve everything and walk me through the best way to run it all the next morning.

At this point, the clock was ticking past five. I was exhausted, hungry, fighting off a headache from squinting at the screen and utterly embarrassed at how much of everyone’s time I’d taken up. I went home, and went to bed.

A good night’s sleep

At 8:55am the next morning, I turned on my laptop, merged my code, and watched as it went off without a hitch.

Eating breakfast, I finally internalised something incredibly important: “failure days” do not make you a failure. Despite how awful I had felt, everyone I asked was completely willing to help. Whether you’re an intern or a senior engineer with 20 years of experience, everyone is always learning, and everyone gets stuck sometimes. The worst thing you can do is suffer in silence. Even with the myriad of problems I encountered, it only took a day and a half to solve. If I’d tried to fix everything on my own, it could easily have ballooned into a week of lost progress.

As an intern, failures feel especially gutting when you’re trying so hard to make a good impression. Remember: engineering is a team sport. Setbacks are hard, but they can be dealt with. So when you hit your own worst day, just take a deep breath, ask for help, and keep going.

Featured image from WOCinTech on Unsplash

Breaking the coding doom loop: lessons from my intern project

Posted by on August 6, 2024

Tell me if this sounds familiar. You get a project idea that you’re excited about, create a new GitHub repository, and dive headfirst into coding. Instead of coming away with your new pride and joy, you end up with just another repo in your archive. Well, this has happened to me too, too many times! 

With my university projects, I could always produce a good piece of work. But university projects didn’t teach me how to see a project through to the end. The impending deadline was enough to get the project past the finish line. However, sticking with a project without that deadline stressor is an entirely different challenge. My internship project taught me how to rise to that challenge.

Dig into the ‘why?’

A key factor in staying motivated is clearly understanding why you are working on your project. Although personal curiosity is a good starting point, knowing the value of your project will keep your momentum going in the long run. 

With that in mind, here’s a rundown of my internship project. My ultimate goal was to help the marketing team with attracting more customers to join FreeAgent by revamping how we give first-time users introductory deals, such as discounted subscriptions for their first few months. 

When you are building something for someone to use, you want it to be good quality. I made a point of reaching out to the marketing team with questions to ensure that what I was going to build would be useful for them. The better you understand the benefits your project will bring, the easier it is to stay motivated and bring the project to new heights. 

Create building blocks with user stories 

Once you know your intentions for a project, you need somewhere to offload your ideas from your mind. Otherwise, when coming back to a project after a break, resuming work could feel daunting when you lack an organised plan. User stories are a powerful tool in solving this very issue.

User stories help you translate your unformed idea into small, manageable steps that will deliver value one stage at a time. User stories are typically broken down into two parts: the ‘why?’ and the ‘how?’. The ‘why?’ section is where you describe why implementing this feature will matter to users. The ‘how?’ is your acceptance criteria. The acceptance criteria will explain how you will know when you’ve reached your goals and delivered value to users.

I found that user stories are a great way to achieve more frequent wins. If you try to tackle all the user requirements in one go, every setback will be a big hit to your productivity and motivation. By using short and sweet user stories, each task you tick off will give you that dopamine hit to keep going.

User stories also let you develop a Minimum Viable Product (MVP). MVPs are extremely useful for distinguishing which features need to exist now, and which can be built later. By dividing my user stories into incremental phases, I got the best of both worlds. As I have limited time as an intern, it’s reassuring to have future phases planned out for someone else to iterate on. This gives me confidence that my brainstorming has been valuable, but also that I will have a solution to show after my internship.

Getting your hands dirty: code spikes and prototyping

While planning is important, you don’t really know how things are going to work until you start coding. This is the purpose of doing code spikes. A code spike gives you a playground to explore your codebase, tackle uncertainty and refine your plan within an arranged timeframe. I often start working on a code spike by aiming to implement a user story. When I run into a problem, I revisit the user story and decide if it’s a problem that’s vital to solve, or if spinning off a new user story would work better. The off-the-cuff nature of code spikes lets me work out the code and iterate towards a more solid solution. Unexpected scope creeps don’t leave me blocked as I know that I can return to my user stories and adapt. 

Code spikes also keep your users involved when you use them as a space for prototyping. Code spikes give you the freedom to Frankenstein together your code and get a picture of what the project will look like. Even if you are the only end user engaging with a prototype, seeing your project being functional helps to gauge your feelings on how your work is shaping out. 

I used a code spike to mock up a user interface (UI) for introductory offers and sent screenshots to the marketing team for feedback. Making the UI through a code spike instead of a mockup meant that, not only could I give the marketing team something to look at, but I could also have code to look back on to see how I built the UI. Visuals help users realise what they want, and the prototype helped me understand the marketing team’s needs through what I coded. 

The magic formula? 

Failing to plan is planning to fail. So, next time I start a personal project, I’ll put my experiences into practice. This means mapping out my ideas and breaking them down into small, manageable steps to stop the project from getting too overwhelming. Keeping that plan in mind, I’ll use that hands-on exploration with code spikes and prototypes to help me continually refine my vision for the project.

The most important thing for me to remember for my next personal project is the reason I started the project: you start projects to improve your or other people’s lives and make their day-to-day a little better. Adding value to the world is a skill worth developing.