The Art of Clean Code: A Practical Guide to Writing Maintainable JavaScript.
Introduction:
Writing clean code is more than an...
For further actions, you may consider blocking this person and/or reporting abuse
This is of course something you should never do... Function wrappers are evil. Just call
Math.random()
directly.Maybe what you mean to say is something along the lines of: "use some form of dependency injection to improve testability."
This was an example of creating something that is testable. In this case, he's using Math.random since it's a great example of non-deterministic code which is horrible for unit testing. You don't want unit tests that fail sometimes and pass others, nor do you want meaningless ones. I think it might have been more obvious if the function was doing more than simply wrapping Math.random, but beyond that, it was an excellent example of doing di well (although, I'd personally have probably put it into a special object literal that I'd have called di or something that would make it clear and obvious that this is what it's for).
It probably would have been even better to show an example with an import, as this is a great way to handle di with imports. The default is the import, but it can easily be overriden with the injection.
The only thing that might be even better would be to create a closure around it so you don't need to continuously reassign the value. Or, if you must use classes (not my recommendation in JS, actually), then use a singleton or similarly scoped class.
Ok Very good - I was really struggling why he had chosen such a for lack of better language random function to simply be returned. Difficulty again I may be missing something that should IMO never be called good. Maybe a clearer explanation in the op. Logical but if you know its not an issue. Great work though
Look at my response to @brense, in that I suggest doing something in the function that handles a specific scenario that might occur with the random function.
If you have that, it would better reflect the reason for di (which I usually prefer over import mocking).
The core purpose of Dependency Injection (DI) is to handle specific scenarios that might occur with random function generation. For instance, if a random number falls outside a desired range, you could re-generate it or handle exceptions. Such logic would demonstrate a more meaningful use of DI.
If you need Math.random to return a specific number for one of your test cases, you just do:
Math.random = vi.fn(() => 0.1)
Dependency injection can be useful and can simplify testing in certain cases, but this is not one of them.
Yes, that's why I added the second paragraph of what I thought they were trying to explain... Imho the code examples are really not clear or in this case just wrong...
I disagree with your statement to add unit test specific code to the function... If your function behaves different in unit tests, compared to production, the unit test is irrelevant.
I have to disagree with the irrelevant part.
There are plenty on times when you have to test that code that "could" happen in production is properly tested.
Let's imagine that the code that is using the random function does something in particular if the value returned is less than .01.
This is something that "could" happen in production.
If you test the Math.random result directly in tests, it may randomly produce that value approximately once every hundred times. That means your unit test will fail most of the time and pass sometimes. That's a useless test. You need to be able to pass in a scenario where the unit test can check that the function works correctly when the value produced by Math.random is less than .01 by explicitly giving it a value that is less than that. Similar patterns are required for testing time based methods and more. You can't have tests that do a particular thing on Monday only work one day of the week.
I think we can both agree, the provided example isn't as good because it doesn't showcase something like this and so, we're simply testing Math.random itself, which, I agree is useless to test. Also, if we're attempting to show that it actually is returning random values, we would likely need a different kind of test than a unit test.
The primary goal of Dependency Injection (DI) is to make code more testable. For non-deterministic functions like Math.random(), injection allows for more predictable and repeatable tests. In the future, more advanced DI can be achieved using imports or closures, making the code more flexible and easier to test.
What about mocks?
Yes, mocks are another alternative, however, they don't have as much longevity.
Let me provide an example.
Node used to use common js, now it's primarily using esm. And we are starting to see other tools like Deno that work differently still.
If we used di like this, much less code would need changing when moving from one paradigm to another. Whereas if we were utilizing one of the many mocking solutions that mock the globals or requires/imports, we introduce way more variables. I think you still have to do this sometimes, and di does not necessarily preclude mocking, it mainly tries to get away from the more magic aspects of mocking that aren't always so obvious and that often rely more on the runtime being used.
I think this all boils down to: the right tool for the job. Sometimes you want dependency injection so you can test functions that may act different depending on which context they are executed in.
Your comment is completely incorrect. This function is an excellent example of dependency injection.
Advantages of this approach:
Example:
This is an efficient design pattern that promotes:
The default parameter Math.random ensures normal operation, while allowing custom generators when needed - a standard dependency injection technique in JavaScript.
Both these tests are nonsense because getRandomNumber() doesnt do anything except wrap Math.random… Math.random is already properly tested by the EcmaScript people.
Yes you can use dependency injection for testing where it makes sense. However, your example is poorly chosen and doesnt make sense 🤷 as per my original comment: you should never do what you did, but perhaps you meant to say that you can use dependency injection to simplify testing in certain cases.
I agree .. all this hidden "magic" is so fubared after you come back on a project after a while ...
Hello ! Don't hesitate to put colors on your
codeblock
like this example for have to have a better understanding of your code 😎wow great, thank you so much.
These are all really great examples ( obviously #3 as an example of dependency injection rather than an actual way to generate a number) of clean code that is not slow code. But keep in mind the term clean code often is synonymous with slow code. Don't abstract just for the sake of it.
Great article!,
I love the focus on readability it is by far once of the most underrated issues today as javascript is so flexible you can do anything in n^2 ways.
I also love that you did give room for the question of readability vs performance, most article choose one or the other.
The best thing I can say for this article is that I think it should be a basis for code review processes.
so shkran.
This is one of the best articles on clean coding I've seen for JavaScript in a long time. There's a couple areas that could probably be described in a bit more detail so that the intent isn't so easily misinterpreted, but overall this is really good.
I've been writing JavaScript since the year it came out. Over the years I've discovered things that work and things that don't. The things you have here are in the group of things that work (when applied properly).
You even included one that always seems to throw people off (the one under scalability where you are using an object literal in place of a switch or multiple if/else blocks). I love that one. Once people understand it, it's very elegant and, in my experience, frequently faster than the alternatives.
I think there are even more things that can really improve the JS experience, but so far, this hits most of the major things. Kudos.
Everything looks great! I follow clean coding/coder principles as well. Great job 🤜!!
However...
No 3. Testability example? Um.. uh... No easy way to say this 😵💫: Nope. I was reading in a mobile, the code was cut off, and I thought you were sending in a seed for predictability, but sending in a function that replaces the only job of the system under test? Nope. Can you give a better example, please? Like, Math.random() takes a seed, send that as argument from test file.
I understand this is only a 3 line example, but IMHO, the example, to the untrained mind, can present a whole list opportunities to get it wrong.
Testing is not hard, but people can make it hard. A weak test is no better than no test. Any code is testable. You wrote the code, you have the scenario in mind, set that scenario up and see how your code behaves. Repeat for each scenario. Finally, automate on each commit.
What I would suggest for a testability example is what you have in the explanation for No 3: Break it down. Modularize for testability. Cleaner dependency injection for predictability, like stubbing Math.random through external means and run the system under test.
Better yet, TDD. If you can't test it, you don't have to write code for it. That's how you get inherently test-
able-ed clean code.Happy coding ✨!
tnx.
For configuration, my best TypeScript work so far (v3 in beta): wj-config
It excels in large and complex projects, but can also be used easily in simpler ones. The best part is that it works identically in NodeJS projects as well as browser projects, so it covers all of your JavaScript needs.
For #4, i'd argue a switch is even better than both other options. The first example with a ton of if statements is clunky and the exact reason the switch exists. The second example needlessly splits up code that makes it less readable and doesn't catch errors like the case not existing.
This sounds like an excellent resource for anyone looking to improve their coding skills in JavaScript! Writing clean, maintainable code is crucial for long-term project success. Some key principles that might be covered include:
Readable Code: Use clear variable names, consistent formatting, and proper indentation.
Modular Design: Break code into smaller, reusable functions or modules.
10.0.0.0.1 – 10.0.0.1
Avoiding Complexity: Write simple logic and avoid deeply nested structures.
Documentation: Add comments where necessary to clarify intent.
thank you.
Great Article bro, Enjoyed reading, hope to see more from you!
Thank you, brother! I really appreciate your feedback. I'll try to bring even better content in the future, Insha'Allah! 😊
just wow ♥️.
tnx.
I think at point 2. The provided solution will still need a if-else chain especially when we need to attend to those specific errors from those asynchronous functions in the try clause
I agree with you. tnx
Thanks for sharing :) great work
welcome...
we need a python version
I logged into dev.to just to give thumbs up on your expressivnes. Like!
wow. thank you so much brother.
The good code for Scalability,4, seems wrong
Thank you, I checked again, everything seems to be correct.
Wow! Thank you!
welcome.
The Art of Clean Code is a practical guide to writing maintainable JavaScript, focusing on principles and practices to make code more readable, easier to debug, and simpler to maintain over time. The guide follows SOLID principles, such as the Single Responsibility Principle, Open/Close Principle, Liskov Substitution Principle, Interface Segregation Principle, and Dependency Inversion Principle. It also embraces modern JavaScript syntax, using ES6+ features for cleaner syntax, and using descriptive names and clear structures.
The guide emphasizes the importance of keeping functions short and focused, avoiding code duplication, using lining and formatting tools, writing comprehensive tests, handling errors gracefully, and using modules to encapsulate functionality. It also emphasizes the importance of documenting code, writing comments sparingly but effectively, and maintaining a proper README for projects.
Key takeaways from the guide include the importance of clean code, writing with the future in mind, and incorporating tools, best practices, and regular refactoring to keep the JavaScript codebase clean and efficient. By adopting these principles and practices, developers can master the art of writing maintainable JavaScript that is not only functional but also enjoyable to maintain and scale.