What is the DRY principle? Why is it useful to follow?

DRY stands for Don’t Repeat Yourself.

It is a very general principle that everyone should keep in mind when writing software. It means that we should avoid repetitions in our code as much as possible. By repetitions we do not necessarily refer to a set of lines or whole lines, it could just mean a string literal, a number, or any smaller fragment of code.

Let’s see a simple example where we use a short formula to calculate a value.

int result = (pointsInFirstRound + pointsInSecondRound) * bonusMultiplier;

It is possible that this formula will be used in multiple places. In order to follow DRY, you should extract it to a method.

private int calculateResult(int pointsInFirstRound, int pointsInSecondRound, int bonusMultiplier) {
    return (pointsInFirstRound + pointsInSecondRound) * bonusMultiplier;
}

You can call this method any number of times.

Advantages of DRY

Less work

By not repeating code, you have less work to do. If you have a logic that spans 10 lines and you extract it to a method. You will need 1 line instead of 10 lines at every occasion where you use it.

Better maintainability

There is always a chance that your code will need to be changed because a bug is found and you need to fix it or a change request came in. If you have the code in only one place, it’s much easier to modify as you only have to do it once.

Less chance to break the code

If you have to do a change in 10 places, there is a chance that you’ll make a mistake. If the logic is only in 1 place, you have a much higher chance of getting it right.

Better testability

If you extract a piece of code to a well-separated and reusable unit, then unit testing it is easy. You can you test the extracted code and you can mock that logic at every occurrence.

If you have that block of code in several places then you have to take it into account at all of its occurrences. So using DRY makes testing easier.

Better readability

As a result of following DRY, you will have less and better-structured code that is easier to follow.

Disadvantages of DRY

DRY is a great principle to follow but if it is overused it can be a source of issues. If you are trying too hard to make sure that nothing is repeated, it is possible that your code will become too complicated and hard to understand.

You have to weigh the benefits of DRY. Sometimes it’s better to repeat some code if it clearly helps readability a lot.

Soft Skill Interview Questions About Junior Developers in the Team

You are the coach for several juniors developers. What practices do you use to improve them?

The techniques that can be used could differ based on a lot of factors including the experience of the junior or how much time do they have for training, but let’s see some general tips that can be used.

Teach them how to teach themselves

For a junior developer often the biggest roadblock is not knowing where to find the required information. Developers should be already familiar with the use of Google and even know about some commonly used sites like StackOverflow. However, it might not always be obvious how to search for the required information, what keywords to use, which information sources are the best, and which should be avoided. Giving some tips on these topics, helping in some concrete cases can show them the right direction.

Point them in the right direction

In case of a question or issue, don’t always tell them the exact solution, but rather point them into the right direction. Of course, it depends on the deadlines how far you can go with this mindset.

Encourage collaboration

If there are more of them, encourage collaboration between them. Tell them to share their ideas and be ready to help each other whenever needed.

Be available

Because their lack of experience there will be roadblocks where they would be stuck for a long time without outside help. Make sure that you are available as much as possible and let them know when you are busy (e.g. in a meeting).

Be prepared to be interrupted often. Most good leaders can free up time for questions usually immediately or in a few minutes.

Delegate simple questions

Developers have different levels of skills. Each has areas where he is more knowledgeable than others, even if they are junior.  If it is probable that another developer can help out in a question, delegate it to him. This way you save time and encourage collaboration as well.

Project knowledge bases

If they are already on a project, share information about project knowledge bases with them as early as possible. Call their attention to the most useful pages/articles for starting out and also mention what information should be avoided because it’s outdated (this happens in every project…).

Do code reviews regularly

Inexperienced developers can make mistakes that require a big part of the solution to be rewritten. It’s best if these turn out as soon as possible and not after 5 days of work. Do code reviews as often as it makes sense.

Involve everyone in the code reviews

In the beginning, probably you will be the one who is giving the most feedback on their code reviews but make sure that they check each other’s code as well to learn from their mistakes and incorporate new ideas to their code.

Always explain

If they did something the wrong way, always explain to them why is your proposed solution is better, what are the advantages of it. Try not to make comments on code reviews too short. You may understand each other from short sentences with someone you have been working with for a long time, but for a new developer, a more thorough explanation can prevent further questions.

You are the technical lead on a new project and got only junior co-workers. What kind of problems do you see? How do you handle them?

Basically, you can use some of the ideas above to improve their skillset, but here are some sections from the problems perspective.

Missing experience

They will have limited experience, so they will need more time to implement tasks. Make sure you take this into account when estimating the length of the tasks. If you give ample time to implement a good solution and learn in the meantime, their skills will improve rapidly.

Organize knowledge transfer meetings if possible.

Missing domain knowledge

Refer them to good documentation and make sure that they understand what they are working on.

Organize knowledge transfer meetings if possible.

Not understanding client requests

We all know that clients don’t always express themselves clearly. Call the attention of the new guys to always ask if something is not clear in the requirements because wasting time on a different implementation can be hurt the project.

Unfamiliarity with processes

At every project, there are some processes that everyone needs to follow. Like what branching strategy to use, or how to handle tickets (which state means what, etc.). Be sure to collect these processes and give the new colleagues a detailed overview of them as soon as possible. It’s best to have it in written form as well, where they can get a refresher is something is not clear.

Review their code thoroughly

You are responsible for making sure that the code committed is in a good shape. Spend extra time on the code reviews, even try out the functionalities manually if needed. Double check if tests are implemented correctly.

Hitting blockers

Make sure to check regularly how they are progressing. Tell them to ask for help if they are blocked on something.

Going on vacation

If you are going on vacation, there will be no one to provide leadership. In the beginning of the project be sure not to go on longer vacations. Shorter ones are much easier to survive without you. Determine the most fitting person to be your deputy and teach them how to lead the team when you are away.

How can you build a team from just junior developers?

If you absolutely must build a team from just juniors, then check the above problems you can face and how to solve them.

Otherwise, if possible try to do some negotiation with other projects/teams if they are able to substitute a team member with a junior guy and give you a more experienced developer. For a team that is going on for a long period of time, getting one new colleague is not that big of a deal because the existing ones can get him up to speed really fast.

What is HATEOAS?

HATEOAS stands for Hypermedia As The Engine Of Application State. It is a technique that is often used in conjunction with REST APIs.

The basic idea of HATEOAS is that when your API returns a resource, the representation should include links that allow the consumer of the API to discover resources related to the current one. It’s similar to the way websites work. When people open a web page, it contains links to further pages, and they can use these links to navigate without any other information.

Let’s see a simple example where we are getting back the details of a user:

{
    "id": 19,
    "name": "JohnDoe",
    "email": "john.doe@gmail.com",
    "links": [{
            "href": "/users/19/comments",
            "rel": "comments",
            "method": "GET"
        },
        {
            "href": "/users/19",
            "rel": "delete",
            "method": "DELETE"
        }
    ]
}

As you can see we have a links array, which contains two links. One for getting the comments made by the user, and one for deleting the user. Because these links are provided, the consumer of the API do not need to know them. It only has to know where to find them. And of course, it needs to know the possible actions about a resource. In this case, it needs to know in advance that a user has comments and that it can be deleted, otherwise, the rel attribute wouldn’t make too much sense to the client.

This is not the only syntax that can be used for providing the links, but it’s a fairly standard one.

Here is a more real life example from the PayPal API:

{  
  "links": [{
    "href": "https://api.paypal.com/v1/payments/sale/36C38912MN9658832",
    "rel": "self",
    "method": "GET"
  }, {
    "href": "https://api.paypal.com/v1/payments/sale/36C38912MN9658832/refund",
    "rel": "refund",
    "method": "POST"
  }, {
    "href": "https://api.paypal.com/v1/payments/payment/PAY-5YK922393D847794YKER7MUI",
    "rel": "parent_payment",
    "method": "GET"
  }]
}

Why use HATEOAS?

The biggest advantage of this approach is that consumers of the API do not have to hardcode the API URLs into their code. They just have to know where to find them. This allows the API developers to freely change the URL structure as it won’t break the clients.

Limitations

In an optimal case, HATEOAS would allow a consumer to start from a “root” resource and just by going through the links, discover the URL for any endpoint and use it. To an extent, it can work. For example, if we are trying to get the likes received by a user for a specific post, a client could automatically

  • GET the user
  • GET the posts of the user
  • GET the specific post of the user
  • and finally, GET the likes for that specific post

All this based on the links returned in the response for each request.

This approach would not work if we need some extra information to be able to submit the request. For instance, if I want to create a post for a user, I need to know what fields do I have to submit. I can get the target URL from the response, but getting all the information about the fields would be pretty tricky. Even if I would get back the list of fields in the response, I would need to have a logic in place that can determine the correct value for each field.

Summary

The HATEOAS technique can be very useful if we are not trying to overuse it. It’s great when we would like to automatically discover content/endpoints to decouple the clients from the API. But we should not try to construct an API that is fully describing itself using this technique because in most cases, this would not be possible in a dynamic way.

Exception Handling in Java

Exception handling is the process of handling exceptional situations disrupting the normal flow of an application. This topic is fairly small and even newcomers to Java will come across exceptions pretty soon in their career, so knowing how you can handle exceptions is a must have even for people who consider themselves “junior” java developers.

Java provides a well thought out, object oriented way of handling exceptions. Let’s see the details in the following sections.

Exception handling overview

As I mentioned above, exception handling is the process of handling unexpected situations. These situations can happen for a lot of different reasons. For example, the programmer used nonexisting indexes on an array or tries to call methods on a nonexisting object, the database server could be down, or we could have lost network connection.

In Java, when a situation like this happens, an exception object is created and the JRE will try to find a piece of code that can handle the exception. This process is called throwing an exception. The exception object itself contains a lot of information about what happened. Where did the exception happen, what caused the exception and a complete stack trace that is basically the call hierarchy that led to this exception.

The logic for finding the piece of code that can handle the exception is pretty straightforward. It starts from the method where the exception is thrown and checks if that method can handle the exception. If it cannot handle it, it will go up the call hierarchy until it can find a suitable handler or the end of the call hierarchy is reached. When a handler is found we call it catching the exception. If no handler is found in the call chain, then the application will terminate and print information about the exception.

So now you have a general overview, let’s see the specifics.

Exception hierarchy

The first important part about exceptions is knowing what classes represent them in Java. The hierarchy looks like the following:

The starting point of the hierarchy is Throwable. All exceptions must be the descendant of this class. There are two direct child classes of Throwable: Error and Exception. Also, we have a special type of exception called a RuntimeException.

Exceptions that are of class Exception or any of its subclasses (except RuntimeException) are called checked exceptions because you have to do something (we’ll see later what this is) with them in your code if there is a possibility that they are thrown. On the other hand, exceptions that are instances of RuntimeException or one of its subclasses are called unchecked exceptions because you are not forced to deal with them in your code, but of course, you have the possibility. Errors are usually exceptions that the application cannot recover from.

Checked vs unchecked exceptions

There are two fundamentally different exceptions in Java: checked and unchecked exceptions.

Checked exceptions (Exception and its subclasses)  are exceptions that usually happen because of some outside circumstance. For example, you are trying to open a file but someone has deleted it. Or you are trying to access a database, but the server is down. Most of these situations although not necessarily common, but has a pretty significant chance of happening at one point or another in the life of your application so you have to be prepared for them so your app can act gracefully in these cases.

If your code has a statement that can throw a checked exception, you have to do something with it. You have to either:

  • Catch the exception and handle it in a try-catch block.
  • Declare the exception in the header of your method using the throws keyword.

If you are not doing one of the above, you will be presented with a compilation error.

Unchecked exceptions (RuntimeException and its subclasses) are mostly caused by programming mistakes. For example, you are trying to access a nonexisting index in an array or you are trying to call a method on a null value. Because these can happen at almost any line in your code, handling them at every point would be really inefficient. This is why it is not mandatory to handle them. It would cause a lot of overhead and your code would be unreadable because of excess exception handling logic.

Of course, it won’t hurt if your application can handle these types of exceptions as well. A good practice to this is to handle them somewhere higher in the call hierarchy in a central place. This way, you only have the handler logic at one place but you are doing something with the exception and don’t let the situation escalate to a point where the user sees a stack trace.

The try-catch block

In Java, you can use the so called try-catch block to handle exceptions. This consists of two blocks. The try block contains the code that can throw the exception and the catch block contains code that will handle the exception. Let’s see a simple example.

public class CustomException extends Exception {
}
RiskyClass riskyClass = new RiskyClass();

try {
    riskyClass.performRiskyAction();
} catch (CustomException e) {
    // Handle the exception.
}

As you can see the try block contains some code that can throw an exception, and there is a catch block that will catch any CustomException thrown in the try block. For catch blocks it is mandatory to specify what exceptions it can catch.

Catching multiple exceptions

You can have multiple catch blocks if needed, like this:

try {
    riskyClass.performRiskyAction();
} catch (CustomException e) {
    // Handle the exception.
} catch (IOException e) {
    // Handle the exception.
}

In case of multiple catch blocks, the JVM will start from the first one and goes until it finds one that can handle the exception. A catch block can handle an exception if the exception specified matches the thrown exception’s class or is a parent of it.

There is an important rule to the ordering of catch blocks. You have to start with the most specific and go towards the least specific. This is because if a less specific one would catch all the exceptions in the beginning, it would be impossible for the code to reach the remaining catch blocks. Here is an example of a correct ordering:

try {
    riskyClass.performRiskyAction();
} catch (CustomException e) {
    // Handle the exception.
} catch (IOException e) {
    // Handle the exception.
} catch (Exception e) {
    // Handle any Exception.
}

The first common superclass for CustomException and IOException is Exception so they can be in any order, but Exception must be specified after them because it is less specific.

Catching multiple exceptions in one block

Since Java 7, you can catch multiple exceptions in a single catch block by separating them with the pipe character. Here’s an example:

try {
    riskyClass.performRiskyAction();
} catch (CustomException | IOException e) {
    // Handle the exception.
} catch (Exception e) {
    // Handle any Exception.
}

The only rule for the exceptions specified in the catch block is that none of them can be the other’s child/parent class as it would mean unnecessary code.

The finally block

You can extend the try-catch block with a finally block. Actually when you have a finally block, you can even omit the catch part.

In the finally block you can specify code that you would like to run regardless of whether an exception was thrown or not.

One of the most common cases is when you are opening a resource (like file stream) in the try block and you would like to be sure that it will be closed even if an exception happens. But that is not the only case. For example, the following code prints how long did the method take to complete in case an exception happens or not.

long startTime = System.currentTimeMillis();
try {
    System.out.println("We are in try.");
} catch (Exception e) {
    System.out.println("We are in catch.");
} finally {
    System.out.println("We are in finally.");
    System.out.println("Total time: " + (System.currentTimeMillis() - startTime) + " ms");
}

Remember, the finally block always executes except when:

  • System.exit() is called.
  • The JVM crashes.
  • The try block never ends (e.g. endless loop).

Nested exception handlers

You can nest try-catch-finally blocks as deep as you would like. So you can have these blocks inside another try/catch/finally block.

Using try with resources

Before Java 7, it was pretty cumbersome to use some resources that needed to be closed after usage. A resource like this is a BufferedReader that uses a buffered input stream to read characters from for example a file.  Let’s see an example how much code did you need to use it.

BufferedReader bufferedReader = null;
String line;
try {
    bufferedReader = new BufferedReader(new FileReader("file.txt"));

    while ((line = bufferedReader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    e.printStackTrace();
} finally {
    try {
        if (bufferedReader != null) {
            bufferedReader.close();
        }
    } catch (IOException ex) {
        ex.printStackTrace();
    }
}

You can see that a good amount of code was needed just to handle the closing of BufferedReader to free up resources taken by it. You can see that this is boilerplate code as you have to do it the same way every time you are using a resource that needs to be closed.

Java 7 came to the rescue by introducing try with resources. It is basically a special version of the try block where you can specify the resource that you would like to use and the JVM will automatically close it for you.

String line;
try (BufferedReader bufferedReader = new BufferedReader(new FileReader("file.txt"))) {
    while ((line = bufferedReader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    e.printStackTrace();
}

As you can see, we could reduce the code to about half of its original size. You can specify as many resources as you want by separating them with semicolons. The only requirement for an object to be listed as a resource is that it has to implement the java.lang.AutoCloseable interface so the compiler can be sure that the classes used to implement a method know how to close the resource.

Declaring unhandled checked exceptions

If one of your methods can throw a checked exception and it is not handled in the method’s code, you have to declare it in the header of the method using the throws keyword.

public void testMethod() throws IOException {
    String line;
    try (BufferedReader bufferedReader = new BufferedReader(new FileReader("file.txt"))) {
        while ((line = bufferedReader.readLine()) != null) {
            System.out.println(line);
        }
    }
}

It is also a good idea to document these exceptions in JavaDoc like this:

/**
 * Test method.
 *
 * @throws IOException Thrown when the IO operation is unsuccessful.
 */
public void testMethod() throws IOException {

Throwing exceptions

Not only code that is coming from external libraries can throw exceptions. Using the throw keyword you can throw an exception any time you want. This is actually pretty often used for signaling to the caller that an exceptional situation has happened in your logic.

public void divide(int a, int b) {
    if (b == 0) {
        throw new IllegalArgumentException("You cannot divide by zero.");
    }
    // ...
}

public void performBusinessLogic(int input) throws CustomException {
    if(input < 0) {
        throw new CustomException("Cannot perform operation with input less than zero");
    }
    // ...
}

In the first example we are throwing an unchecked exception so it’s not declared in the method’s header. In the second example, a checked exception is thrown so we needed to declare it.

Wrapping exceptions

When you catch an exception it can often be a good idea to throw a new exception in the catch block. In these cases you should wrap the original exception in the new one, so when the new exception will be logged, the original one is logged as well.

try {
    throw new UserNotFoundException("Cannot perform operation with input less than zero");
} catch (UserNotFoundException e) {
    throw new OtherException("Other exception", e);
}

We called this constructor:

public UserNotFoundException(String message, Throwable cause) {
    super(message, cause);
}

Custom exceptions

When you are throwing exceptions, those often should be custom exception classes made by you. Here are some examples:

public class UserNotFoundException extends Exception {

    public UserNotFoundException() {
    }

    public UserNotFoundException(String message) {
        super(message);
    }
}
public class UserAlreadyExistsException extends Exception {

    public UserAlreadyExistsException() {
    }

    public UserAlreadyExistsException(String message) {
        super(message);
    }
}

You can extend any of the existing exception classes (even yours) and create custom exception hierarchies.

Making and throwing custom exceptions allows you to be as specific as you want about the situation happening and you are able to handle the different exceptional cases in different ways.

Best practices

Logging

Properly logging the reason of the exceptions is one of the most important ways to keep your code as easily debuggable as possible. When you are catching an exception it is always a good idea to log a message stating the most concrete info that you know about the exception and you should also print the exception object itself as well. This will print the stack trace as well into the logs. It will come in very handy when someone wants to debug the code.

Of course, printing the exception information to the user is a bad idea. Usually, he won’t understand what is a NullPointerException for example and it can give away information to hackers. For users just print a friendly message that something went wrong and you’ll look into it.

Fail fast

If a method will throw an exception it is best if it throws as early as possible. So for example, if you have some conditions in your method that will result in an exception if not fulfilled, it’s best to check them as soon as you can, so the method won’t run unnecessarily for a longer period of time. This makes debugging easier as well.

Catch only when able to handle

You should only catch an exception when you can act on it properly. Just catching the exception and doing nothing will make debugging your application really hard when an exception happens.

Naming conventions

Your custom exceptions should be named in a way that they end in “Exception” like “CustomException”.

What is REST?

REST stands for Representational State Transfer. 

It is an architectural style (or software design pattern) that is used for designing web applications or web services. Well, that is not entirely true, because REST can be used in case of any networked application, but because the overwhelming majority of these are web applications/services, we are going to refer to them in this manner.

When you are using REST to build a web service, you can say that it is a RESTful web service. If you are referring to the API that this web service provides, it’s a RESTful API.

REST Guidelines

REST does not have a specification or concrete rules on how it must be implemented. It is a collection of guidelines that you can use if you would like to develop a RESTful API. You can choose to follow all or just a subset of the guidelines depending on how RESTful you want your API to be.

This approach might become more clear if we compare it to the SOAP communication protocol that is also a commonly used in case of web services. This protocol uses XML format for communication. The request/response formats need to follow strict rules and there is a separate descriptor language called WSDL to describe the formats used by the web service. REST, on the other hand, does not tie your hand regarding what format you should use (XML/JSON, etc.), what should the request or response contain. It is just a set of guidelines to use when building your web service. Although it’s worth mentioning that JSON is the most commonly used format.

So let’s see what are the guidelines that we need to follow if we would like to implement a RESTful web service:

  • Objects as resources: Server objects are treated as resources and are exposed using a uniform URL structure and are manipulated using a common set of verbs (HTTP request methods).
  • Well chosen HTTP status codes
  • Stateless client-server communication
  • Representations: Representations of resources are not dependent on which resource we would like to get, but it is determined by the request (with the use of request headers).
  • Cacheable resources

Objects as resources

In regular web applications that you can access through your browser, you can usually see URLs that refer to some action like:

  • /users/show-profile
  • /results/processResults.php
  • etc.

As you can see these describe some kind of action, they tell the server what it needs to do.

REST has a quite different approach to this. It does not talk about actions, it talks about resources. Objects on the server side (e.g. blog posts, users etc.) are treated as resources, and you can reference them using a standard URL format. The operation you would like to perform on a resource is determined by the HTTP method that you use.

The purpose of the HTTP methods are:

  • GET: Retrieve a resource.
  • POST: Create a resource.
  • PUT: Update a resource.
  • DELETE: Remove a resource.

We can differentiate instance and collection resources depending on whether we are talking about one or more objects. Let’s see some examples of the standard URL structure and the HTTP methods that we can use.

URLHTTP methodDescription
/messagesGETReturn all the messages.
/messages/{messageID}GETReturn a single message.
/messagesPOSTCreate a new message.
/messages/{messageID}PUTUpdate a message.
/messages/{messageID}DELETEDelete a message.
/messages/{messageID}/likesGETReturn all the likes for a particular message.
/messages/{messageID}/likes/{likeID}GETReturn information about a specific like for a specific message.
/messages/{messageID}/likesPOSTCreate a new like for a particular message.
/messages/{messageID}/likes/{likeID}PUTUpdate a like for a particular message.
/messages/{messageID}/likes/{likeID}DELETEDelete a like for a particular message.
/messages/{messageID}/likesDELETEDelete all likes for a given message.

As you can see, some of the URLs are identical, but we are using a different HTTP method, so the server will be able to decide what we would like to do.

HTTP method idempotence

We can say, that an HTTP method is idempotent if issuing the same HTTP request multiple times results in the same outcome as if we have just issued it a single time.

The HTTP methods GET, PUT, DELETE are idempotent, if used properly. Let’s see why:

  • Issuing a GET request multiple times will just return the same response multiple times. No issue is caused by this, it can be easily seen. Of course, if the data on the server changes in the meantime, a different result set will be returned.
  • If you issue multiple PUT requests, you are updating the resource the same way multiple times to the same state. The end result will be the same as if you would have just made one call.
  • Making a DELETE request multiple times won’t cause issues because the requests other than the first one won’t find the resource and will not be able to delete it. So the end result is the same as if there were only one call.

On the other hand, POST is not idempotent as making multiple POST requests with the same content will result in the creating of duplicate resources (with different IDs).

There is catch, however. For the above to be true, you have to design your API correctly. Here are some examples that would violate these idempotence rules and should be avoided when designing a RESTful API:

  • You have a GET endpoint that creates a resource. GET should be idempotent, but this case it is not.
  • You have a PUT endpoint that allows users increase the amount of votes for a picture. PUT should be idempotent, but in this case it is not.
    • Because you increase the votes by incrementing the current vote count, issuing this request multiple times will cause the votes to increase multiple times. Possible solutions:
      • Use POST in a RESTful way by creating a new “vote” object every time a user votes.
      • Use the less known PATCH HTTP method (not the scope of this tutorial).

Through a final example, you’ll easily see how important it is to keep to these rules. When you are submitting a form using a POST request and try to refresh the browser, you are presented with a message asking if you are sure that you want to issue the same request again. This is because of the fact that POST is not idempotent and issuing it multiple times might cause unwanted side effects like buying two products instead of one. For other HTTP methods, there is no such warning, because they are supposed to be idempotent.

Well chosen HTTP status codes

Becuase REST APIs are consumed by computers and not humans, it is important to always use the proper HTTP status code in the response, so the consumer code is able to decide if the request was successful or not. If the operation failed, the status code can tell some details about the reason of the failure and the client code can use that to decide what should it do (e.g. retry the request).

Stateless client-server communication

In case of REST, the distinction between the client and server is important. These need to be independent of each other, so we can easily scale or relocate them to different servers without affecting the other.

Also, REST relies on a stateless protocol (that is almost always HTTP). This means that every request should contain every information that is needed for the server to process it, so the requests must not depend on anything that was sent by a previous request.

If we take this seriously, we cannot even store anything in the session in a RESTful application, the client has to track this kind of information.

In case of APIs that does not require authentication, this is easily feasible. Most REST APIs, however, use some form of authentication. In this case, no storage on the server side is needed if the clients send their credentials with every request. However, it is not always the best way because in this case we always need to re-authenticate the user. Usually, there is some kind of token generated that the server accepts as authentication for a given amount of time. This slightly violates the stateless principle but some sacrifices have to be made if we would like to make a properly working system.

Representations

Each resource (e.g. blog post) can have many different representations. The most common ones are JSON and XML. REST says that the returned representation of the resource is not dependent on which resource we would like to get back, but on the HTTP request, we send. The request should contain information about what representation we would like to get back.

The headers used are:

  • Content-Type: The client and the server both use it to specify the format of the data that is being sent.
  • Accept: The client uses this to specify what format it expects from the server.

Cacheable resources

In REST, resources have to be explicitly declared cacheable or not. Caching a resource can eliminate some of the calls to the server as we can just use the locally cached version.

Documenting a REST API

The first step to a good documentation is to follow the REST principle as much as possible. This will allow users to more quickly understand how they can use your API. Of course, you have to create a documentation as well. There are a lot of various ways how you can structure the documentation, here is one example:

  • Description of the endpoint
  • URL
  • HTTP method
  • URL parameters
  • Data parameters (in request body)
  • Request / response format
  • Success response
  • Error responses
  • Sample call
  • Notes

REST in the real world

When we refer to REST, it can mean different things. As an example, it can be an API that fully conforms to the REST principles or it can be a web application that only uses some ideas from REST.

For example, we could just have some endpoints in a web application that use the REST URL structure and follows the HTTP method usage guidelines but uses the session to store details about the currently logged in user. This obviously violates the stateless principles, because we are depending on data stored on the server side.