Problem Breakdown: Enquiry Support System

· problem-solving

So in a previous article (https://ramihikmat.net/Article/2023-problem-breakdown%3A-contact-us-page) we’ve planned a contact us page allowing customers of our site to contact, enquire and ask questions. We just stored the data and there was no place for us to view the data we’re getting. I highly recommend you read that previous article as we will build on top of it.

This article will address the next step of the product. This article will not cover coding, it is mainly around how can we breakdown the problem into smaller chunks. The main objective is to design the solution without writing a single line of code allowing you to expose hidden or unknown dependencies. It is also a worthwhile exercise for the mind to do occasionally especially if you write code more than you design it.

Let’s start. The requirements are to make a UI showing all requests submitted by customers and allow our internal people to reply to those users directly to their inbox. We will need a way to know that we’ve replied to those users. A request is an enquiry, question or something a user submitted from the contact page.

In other words:

  • View all requests
  • Filter by un-replied and replied requests
  • View reply we’ve sent
  • Mark a request as ignored which would make it disappear
  • Reply to email of user directly
  • Allow to reply more than once

That’s pretty involved. Right off the bat, backend and frontend components do exist here. We will also need to interface with an emailing service to send the email to users.

There is a couple of questions that pop up:

  • How do we deal with who has access to the UI?
  • Shall we have a way of detecting spam and automatically ignoring it?
  • How often is the system expected to be used?

Answering for the first question: let’s assume there is no user authentication here. The system shall be hosted on our internal VPN and only accessible from that VPN’s IPs. Same applies to the frontend.

The second question: will not worry about spam detection. That seems to be an extra requirement that may come in the future but given we recently added the initial request collection system then will need to establish some patterns before we jump onto spam checking. I always suggest we try to justify the existence of a system before planning for it and that always produces less stress over code design and deadlines because there is less moving parts.

Re the third question: the system is expected to be used by one person a couple of times a day. It should not be built to scale since it is just internal and does not affect the critical path of our main business. If this fails then it is not as urgent to fix as if the support page failed for instance. Obviously we should keep it as simple as possible so it doesn’t even fail.

Cool. We already have the data being collected at this point. All the requirements here involve a human interfacing with it. Therefore we can list our possible operations:

  • View support requests
  • Paginated because there maybe a lot
  • Allow to filter by status. A status could be replied, un-replied or ignored.
  • View individual support request
  • Opens up in a new page
  • Shows the entire discussion
  • Shows what we replied and when if any
  • Reply to request
  • Ignore a request

Cool so let’s say the designs are done. We’ve agreed on the operations above and the contract between frontend and backend. Will assume it is a RESTful contract but doesn’t have to be.

Now let’s talk about the frontend breakdown. We can divide it into 2 pages. One to list the requests and one to view an individual request with the ability to reply and ignore.

So right away, we can have 2 engineers on it. One for each page if we want to. You can even go further but such pages don’t seem too complex to build to require a lot of engineers.

The requests page can be broken into the following tasks:

  • Design the skeleton layout
  • Implement the design with mock data
  • Consume the view requests endpoint
  • Implement pagination
  • Implement navigation to individual request page
  • Add filtering of data to be retrieved
  • Hook up frontend to backend once done

While the individual request page can be broken into:

  • Implement the skeleton layout
  • Fetch the entire content
  • Fetch the replies if any
  • Implement the design
  • Add the ability to reply
  • Add the ability to ignore a request
  • Add navigation back to list requests page
  • Hook up frontend to backend once done

Sweet. Now you may ask why not implement the design right away? You can. If the design is simple enough then do it but sometimes it has nuances. A skeleton page would hold the basic elements required to perform actions on the page. This way you can work on the design and functionality in parallel. In other words, the skeleton page task is the blocker for all other tasks. I prefer doing it this way because I’ve found pages to be thought of this way, there is usually multiple elements in a page. While I am working on a piece of functionality then submit a PR for it, I can right away start on another task independent of whether the design is implemented or not.

Another reason why you may want to leave implementing the design as a separate task is sometimes the engineer that works on the functionality may not be the best resource to design the page especially when it may have some strict design requirements. In our case, our page will just be a normal admin page.

I should probably mention that some pages are very simple, breaking them down into smaller tasks can actually be a productivity hit. You may take more time to build as a result. With these of kinds of decisions, always know what type of engineer will work on them and decide accordingly.

Sweet. Shifting to the backend. The most significant piece of functionality is to actually send the reply in email. Each operation described above can be broken down into multiple pieces and picked up by a separate engineer. Will assume that we’ve decided on an email sender and someone from the Product team has made the email template. All we have to do is to call an API and that’s it.

The tasks are:

  • Define skeleton endpoints returning status code 200
  • Design and create the database tables
  • Implement the view all requests with pagination
  • Implement the view single request endpoint
  • Return contact request data
  • Return any replies we have
  • Implement the reply endpoint
  • Construct the email body
  • Call the email provider
  • Persist the reply record in the database
  • Roll back all changes if a failure occurs
  • Implement the ignore contact request endpoint
  • Mark the status of a contact request as ignored

Amazing, all is ready. All requests must be validated according to the contract we defined. I would also urge we do validate on the frontend before sending anything to the server as well.

It must be noted that we’re not doing any authentication. We will need to consider the devops aspect of this which is how do we ensure the user is behind the VPN. First we need to make our backend server check for IP address. It can be done on the app layer which is in the controller layer of each endpoint or at a network layer where we whitelist IPs. I prefer the network layer here because it doesn’t require any code changes to change the IP. It also means the system can be hosted anywhere so for a developer machine we wouldn’t need to have checks to see if the environment is development then don’t check for IP. That just increases the cases we have to consider when we test the system.

With either approach, I recommend doing the same for the frontend. On a network layer, we can just reject requests. Though the frontend can be made to function even when there is no data from the backend. This is something I want to add to our spec even if the requirements didn’t specify it, it is always a good user experience. We should notify the user of an error if they do occur and not present a white page.

Adding automated tests is something I want you to consider. I prefer writing tests whenever we build a CRUD system because data is going to be modified and we better ensure we don’t mess things up using testing. It also holds us accountable to the system. Without automated testing, you will need to manually go through all paths and verify whether they work or not. That manual action tends to become cumbersome as the system gains complexity.

Before we close, we did make an underlying assumption that this system is going to be separate to the contact page. The only shared thing here seems to be the database. Since it is internal, I think that is a good compromise to use the other system’s database. It just means we would need to grant access.

There is always the option to expose the add request endpoint used in the contact page from this backend we designed and that way we can just have one locked database. That is where we need to contemplate over the pros and cons of why a separate system is better or worse. I prefer we keep things simple and only design for best practices when there is legal, scale or organisational requirements.

I think I was able to highlight an issue that’s common with such request/response systems and how you need to deal with such decisions. If the initial plan said that we have huge plans and data must be stored separately then we would’ve had the one locked database from the start. Though that’s also risky, what if the product failed?

Enjoy designing software.