One thing I always pay attention to in code reviews is the naming of the component props.
It is always important to write self-documenting code, but it's never been as crucial as when it comes to prop names. Especially if you're working in a team or developing a library.
Let me give you an example. Once, I had a fellow developer working on a toggle switch component. A fancy checkbox, if you fancy. And he ended up coming up with the following interface:
interface Props {
disabled?: boolean
enabled?: boolean
}Where the disabled prop dictated whether the switch is available for user interactions altogether. And the enabled prop dictated its current state like "on / off".
Obviously, it's a ridiculous interface, and nobody in the world would be able to tell with confidence, what these props do without digging into the component's code. And that's what we want to avoid.
The Concept
The interface should always be understandable at a glance. And it doesn't only apply to the components - the same goes for public class fields and methods, function arguments, config objects etc. Anything that relies on user input. We don't want the user of our code to confusingly scratch his head and to end up having to read through our code in order to figure out what he needs to specify to have it working the way he wants.
And I would not bother to nitpick on variable names and stuff like that inside of the component.
Of course, it doesn't hurt to keep the code clean everywhere, but on a certain level, as long as it gets the job done and doesn't butcher the performance, I (more or less) don't care how it's coded.
When building reusable components (or classes etc...) our goal is to develop a kind of a black box.
The user needs to know what our component does and should not run into problems figuring out how to hook it up. When a user has to open our source code and analyze it to figure out what certain parts of our interface mean, that's a mission failed on our part.
Get it right from the start
And one more thing to keep in mind is the ability of our interface to scale and adapt to new features.
With such things as components (especially in component libraries), once people start implementing them, every prop change is a breaking change.
The same goes for function arguments and even return values. When developing, try taking the hands off the keyboard from time to time and thinking about how will the thing you're working on scale? What potential changes are there? Is your interface ready for those changes?
Let's say, for example, we have a function that handles some fancy login process, and we return a token from it:
interface Credentials {
username: string
password: string
}
function login(credentials: Credentials): string {
// ...
return token
}Ok, but what if a couple of months down the road we realize that now we have to return a token expiration date along with the token? How will we do this? There are already some users who implemented this function, and they expect the return value to be a string.
Sure, we can announce a breaking change and make everyone adjust their code to keep using our function. But it would be a lot cleaner if we left ourselves room for adjustment from the start.
In this particular example, maybe it would be preferable to return an object (even with a single property), instead of a string:
interface LoginResult {
token: string
}
function login(credentials: Credentials): LoginResult {
// ...
return { token }
}The Takeaway
In summary, every time we're developing something for other people to use, we should prioritize crafting a clear and intuitive interface by thinking about it from the user's perspective.
We should not only think about how our components work under the hood, but also how straightforward they are to implement.
Stay thoughtful everyone, and happy coding!