Why you should make sure that your function params are extensible ?
There are few patterns which I usually follow while writing functions, which if I don't usually come bite me in the future. The same are applicable for APIs as well.
1. Making sure that the response is extensible.
Let us say that we have an API for getting a user, after designing the V1 you realise that you would need to add error states / tasks the user is working on in the API itself.
Which ones of the below approach you think is more extensible ?
class DbUser {
name: string;
age: number;
}
function getUser(id: string): DbUser
class APIUserResponse {
user: DbUser
}
function getUser(id: string): APIUserResponse
Definently the second option sounds better as you can simply add error
state / tasks in the UserResponse
object while in the first one we would need to refactor both the frontend and the backend.
What makes things more extensible ? While there is no fixed rule, it depends a lot on the context. The user object is definitely extensible in the context of a user, example you will not think twice before adding an address property above, but it is not extensible from an API perspective, where the expectations would be to deal with things not particuarly specific to the user object. If we were to put in some words
- Primitive types are not extensible.
- Objects can be extensible or not depending on the context.
2. Request being extensible.
While in general function parameters are generally extensible, because nothing is stopping you from adding one more argument to the function call
function getUser(id: string, includeDisabled: boolean): APIUserResponse
But this works out well only if you have 2-3 arguments applicable after which your function signature starts being less readable.
function getUser(id: string, includeDisabled: boolean, fields?: string[], includeTasks?: boolean): APIUserResponse
Here also using objects as request params will lead to both cleaner and extensible code, specially if your language does not support named arguments (like typescript). Take the example of the following call sites. Which one is more readable ?
getUser('1', false, none, true);
getUser({
id: '1',
includeDisabled: false,
includeTasks: true
})
So key take away is for critical functions which you feel will change a lot in the future best to avoid primitive types directly in params and use extensible objects.