5 principles for writing good functions
I'd like share some principles I think about whenever I'm writing a function. Things that will make your work easier.

Search for a command to run...
I'd like share some principles I think about whenever I'm writing a function. Things that will make your work easier.

No comments yet. Be the first to comment.
One of the first problems you have to solve when building your first chatbot, is to create embeddings for your knowledge, store them in a database and find the relevant knowledge for the question. That works well for a while, until you get users that...

Recursion has a bit of a bad reputation. Unfairly I would say. Lets see if I can convince you.

I'm not telling you not to use classes. Some of my best friends use classes. I'm saying that you might not need to.

They are not hard rules that must never be broken, but simply helpful guidance.
It may sound like a trivial topic. Everybody can write a function, but to me, this is what separates good code from bad code. By bad code, I mean code that it's hard to read, hard to test, hard to change and hard to maintain. Writing a good function can also be very hard to do, so it takes a lot of practice.
// ๐ฑ
async function getUserPosts(user) {
return service.getPosts(user.id)
}
// ๐
async function getUserPosts(userId) {
return service.getPosts(userId)
}
Don't pass in more than what the function needs. In this example, we pass in the user object, even though we're only using the id. In a test, you will have an easier time just passing in the id, and not having to pass in an entire user to convince Typescript your code is correct. Also, the naming will become more explicit.
// ๐ฑ
async function getUserPosts(userId) {
return service.getPosts(userId)
}
// ๐
async function getUserPosts(service, userId) {
return service.getPosts(userId)
}
// ๐ฅน๐
function createGetUserPosts(getPosts) {
return async function(userId: string) {
return getPosts(userId)
}
}
const getUserPosts = createGetUserPosts(service.getPosts)
In the first example, where did the service come from? Maybe we're in a class and the service is passed to the constructor way up in the file. Maybe it's one of five dependencies. You're going to enjoy testing a lot more if you write it in one of the latter examples. The third example is using a concept of higher-order functions for achieving dependency injection. This in itself deserves its own article, so be on the lookout for that.
// ๐ฑ
async function getUserPosts(userId, category, date, archived) {
return service.getPosts(userId, category, date, archived)
}
// ๐
function getUserPosts(getPosts) {
return async function(searchParams) {
return getPosts(searchParams)
}
}
I'm not saying I like having many parameters to begin with, but if I have to, I try wrapping them into an object. If my function has four or more parameters, I reach for an object instead. It has a nice side effect of readability when you have optional or boolean parameters.
If you try to keep your functions focused with limited responsibility, this will almost be automatic. My personal preference is having the function fit on the screen. This usually means around 20-30 lines.
// ๐ฑ
function validateAge(age: number): { msg: string } {
if (age < 0) throw new Error('Completely unexpected!')
if (age > 70) throw new Error('Ok boomer...')
if (age > 30) throw new Error('Still too old!')
return { msg: "It's all good!" }
}
// ๐
function validateAge(age: number): ConceivableError | Success {
if (age < 0) throw new Error('Completely unexpected!')
if (age > 70) return 'boomer'
if (age > 30) return 'still-too-old'
return { msg: "It's all good!" }
}
Silly example, and one of the principles I haven't yet fully landed in. But take a look at the first function definition. It's hard to understand what can go wrong inside the function. In the second example, we've at least defined some of the more common error states. You can play around a lot with this type of error handling and go for something like the Either type, which you usually find in more functional languages.
Please share if you have any good principles of your own that you think can be helpful!