Skip to content
:tech
← Articles

AI-translated. Read the original in KoreanEmbedded slides, videos and images remain in their original language.

An Android Developer’s First Steps into Backend Development

Summer Kwak
Summer Kwak

Software Engineer

An Android Developer’s First Steps into Backend Development

Hello, I'm Seohyun Kwak, an Android developer at Buzzvil. In this post, I want to share the trials and errors I experienced when I started backend development, along with the guidance Buzzvil provided to help me get up to speed. I hope it is useful to anyone thinking about onboarding junior developers or helping people move into a different engineering discipline.

0. Motivation

Why did an Android developer like me end up working on a server? The reason was that familiar problem every company faces: not enough people. Of course, nobody simply said, “We don't have a backend engineer, so you do it!” There was work to be done, and I happened to want to try backend development, so I volunteered. Our team, which was developing a new service, consisted of two Android developers, a designer, and a PM—a small, startup-like team. We had planned an experimental feature but had no backend engineer to build it. Everyone on the team, myself included, was impatient to launch it and see the data. So I started with the simple thought, “If there's no one else, I could do it.” I wasn't underestimating backend development. But it was a small feature, with expected usage of around 10% of DAU—though we hoped for more—and I thought I could manage it with help from our existing backend engineers. Buzzvil encourages building and testing quickly, so nobody tried to stop an Android developer who suddenly wanted to build a server.

To people in other roles, developers may all look like people typing colorful text on black screens. In reality, there are many distinct disciplines, and moving between them is uncommon. Still, most developers have at least some desire to build their own service from beginning to end. Last summer, I joined a group of engineers and designers at the company to create a new service. Study groups are very active at Buzzvil, although COVID has recently kept us from meeting. That project gave me a chance to use Go and React, which I enjoyed. Buzzvil actively supports developers' growth and encourages us to take on different roles when we want to.

1. Getting started

When I said I would try backend development, one of our backend engineers offered to help and gave me a guide. Many of the tools were new to me, so I did not understand everything clearly at first, but I could get a sense of the overall development flow.

These are the tools and languages I used:

  • Visual Studio Code
  • Go
  • Docker and Docker Compose
  • cookiecutter
  • Swagger
  • kubectl

Design Canvas

Let's start building a server! But wait—we don't start by coding? The first task was to write a design canvas. Buzzvil follows a microservices architecture, creating and deploying new services as separate microservices. The author of a new service writes a design canvas explaining why it is needed and what it does, then reviews it with other backend engineers and PMs. It includes:

  • The purpose of the service
  • What is in and out of scope
  • APIs
  • Milestones
  • Dependencies on other services
  • Expected request patterns, such as high throughput

Writing the canvas clarifies the microservice's purpose and scope and helps the author and team agree on what to build and how. Defining the scope was fairly easy because we had planned the feature together. Estimating requests was harder, so I referred to many canvases written by other engineers.

After writing the canvas, I gathered about five people, including myself, for a review. We discussed not only why the service should exist but also model names and the broad direction of the database schema. After some revisions, the canvas was finally approved!

API design

The next step was to define the agreed APIs in protobuf. Protobuf is used to define the APIs needed for gRPC communication. Protobuf and the API-first approach had come up several times in Buzzvil's Wednesday developer seminars, so the concepts were not entirely unfamiliar. Buzzvil manages protobuf API definitions in a single repository. When a PR is merged into master, the APIs in that package go through our Swagger-based build and release flow to produce protobuf artifacts and Java HTTP clients. Android developers can then use the HTTP client without writing the API-related code themselves. When I had only consumed those APIs as an Android developer, I hadn't thought much about the process. Writing them myself showed me how much there was to consider. Still, I had seen others do it, so I gave it a try. As the Korean saying goes, even a dog at a school learns to recite poetry after three years: observing others can do a lot for your growth. And to learn good poetry, it helps to be at a good school!

  • Is the API name concise and meaningful?
  • Are the resource names RESTful?
  • Is the API called by the app or used internally?
  • Are any resources unnecessarily duplicated?

One advantage of my client-side background was being able to picture which APIs the app would call, what they would do, and how the screens would be structured and change. We had already agreed on the protobuf function names in the design canvas, so those were straightforward. Choosing RESTful resource names took more thought. When something was ambiguous, I opened a PR and discussed it with the reviewer. After a few rounds of feedback, the API definitions were done too.

3. Developing the Go project

Bootstrap

Now I was ready to start the actual Go project! Getting a project bootstrapped is often the most confusing part. “Where do I begin?” can be the biggest hurdle when trying something new. For Android development, installing Android Studio generally gets you ready to go. With backend development, I had no idea where to start. Fortunately, Buzzvil has templates so newcomers do not have to figure it all out alone. Our engineering team had prepared a project template using cookiecutter. I only needed to enter the resource, package, and project names to generate a Go project. Cookiecutter is a utility for defining project templates. Opening the generated project, I found cmd and internal packages, with usecase, repo, and entity files underneath, as well as go.mod and a Docker Compose YAML file. I just had to add the required logic in the right places. I used VS Code as my IDE, launched a dev container with Docker Compose, and worked inside it. VS Code's Remote Containers extension lets you set up your development environment inside a container. You can read more here. Docker Compose manages the options needed to run containers, including development configuration and the databases required for development.

Presentation layer development

The generated Go project is divided into presentation, domain, and data layers, named server, usecase, and repo respectively. Everyone approaches this differently, but I started with server. I left repo until last, and therefore set up the local database last too. There was not a great deal to do in the presentation layer. Since the protobuf definitions were ready, I could write the code to match those APIs. Cookiecutter had already configured most of the main function, so the first PR was relatively easy to submit.

Testing

Before sending a PR, I needed to check that the code worked. Creating docker-compose.test.yml and configuring Docker gave me a test environment. Instead of actually running the server, I validated it by writing tests. TDD was very convenient, but part of me kept wondering whether this was really enough. On Android, integration tests take a long time, so running the app directly on a device is often easier. Our Android team values testing too, of course, but integration and UI tests are slow and difficult. It is hard to say confidently that code works without ever launching it on a device. TDD was therefore both the hardest part of backend development to get used to and one of the most convenient.

It took a lot of trial and error just to run the first test. I started Docker, built the test-environment image in VS Code, and worked inside it. Getting used to these concepts took time. I was grateful that recently built microservices used environments similar to mine, giving me examples to learn from.

Pull requests

As with PRs on any team, the backend engineers shared plenty of useful tips in their reviews: how to handle errors, which conventions to follow, and much more. It made me think that when a junior engineer joins our team, I should give them more detailed reviews too.

They even congratulated me on my first PR!

For the record, I still haven't received that coffee. Ha!

Domain and data layer development

Only the domain and data layers remained. After finishing the domain layer, I set up the database in Docker for the final data-layer work. It was remarkably convenient: just a few lines of configuration to get a database running. I chose MySQL, but still had to decide which ORM and migration tools to use. At the time, the backend team was experimenting with alternatives to GORM and migrate, so different services were using different tools. After discussion, we agreed to start with raw SQL and goose. Raw SQL was convenient because there was no extra tool to learn, although writing all the SQL by hand was a little tedious. I imagine developers have different opinions on that trade-off.

When I finished the repository layer and received the PR review, I realized I had not thought carefully enough about what counted as domain logic. For example, when retrieving a model from the database, I had put the logic for “create it if it doesn't exist; otherwise return the stored one” in the data layer. After talking with my reviewer, I agreed it belonged in the domain layer. The upsert logic had also made the data-layer interface untidy. Moving it into the domain layer made the data layer much cleaner. The single GetLotteryTicket interface operation became three, but the implementation was better organized. Separating domain logic properly matters in Android development too, so I was glad to learn more about it here. Android adds views to the mix, making the code more complex and much larger. This experience reminded me to think even more carefully about how I separate layers there.

| Before

type Repo interface {
	GetLotteryTicket(accountID int64, appID int64, lotteryKey string) (*LotteryTicket, error)
}

| After

type Repo interface {
	GetLotteryTicket(accountID int64, lotteryID int64) (*LotteryTicket, error)
	GetLotteryByKey(key string) (*Lottery, error)
	SaveLottery(lottery *Lottery) error
}

4. Deployment

I barely had to worry about deployment because Buzzvil's DevOps team handled it for me. Thank you! They took care of the many tasks needed to get the code deployed, including CI configuration and production and staging databases. What I appreciated was how much of this was already templated. Rather than configuring everything from scratch, they could adjust a few things for each service. Looking back, many aspects of our backend development are templated to minimize human error. That helped me enormously. Hats off to all the Buzzvil engineers who set it up!

5. Looking back

What went well

What I appreciated most was the attention and reviews from other developers. One engineer who had nothing to do with my service even read my code over the weekend and sent me a PR. Although it was not merged, it gave me many useful ideas for working in Go.

Another benefit was learning how backend development and deployment work, and how to inspect logs. I feel proud that when I encounter a suspected bug while working on Android, I should now be able to do some basic troubleshooting myself. It also made me want to send PRs to other services.

What was difficult

The difficult part was the amount of setup required, such as obtaining infrastructure permissions or installing a separate database migration tool. The Confluence documentation was well organized, so following it was generally straightforward. But when I got stuck, I sometimes felt lost. Troubleshooting guidance would be a useful addition to the documentation.

What comes next?

After spending a long time focused on client development, working on a server again was genuinely enjoyable. I cannot claim to have been particularly fast, but I was happy that I finished reasonably close to the planned schedule. It may be a little presumptuous, but if our team needs another service, I now feel I could volunteer to build it. Any chance to collaborate with and learn from engineers across the company is valuable. I do not know whether I will again take something from planning through development and deployment on my own, but if I do, this experience should help me do it better. First, I need to document what I learned. I plan to share those notes with the engineering team.

This also made me want to take good care of backend engineers who try Android development, or junior engineers who join our team. Documentation matters, but I particularly appreciated the feeling that we were working together to build a service. Engineering teams may have a reputation for being impersonal, yet their culture has a significant effect on each developer. I feel fortunate to work in an organization that gives people the chance to try what interests them and encourages everyone to learn from one another.

All articles