One-liners: far more than just one line

Here’s one:

const groupBy = (arr, groupFn) => arr.reduce( (grouped, obj) => ({ ...grouped, [groupFn(obj)]: [...(grouped[groupFn(obj)] || []), obj], }), {} );

and another:

const randomHexColor = () => `#${Math.random().toString(16).slice(2, 8).padEnd(6, '0')}`; console.log(randomHexColor()); // #7a10ba (varies) console.log(randomHexColor()); // #65abdc (varies)

Indeed, they are impressive when done right; a nice way to show off language mastery.

But exactly is a one-liner? Is it really code that takes only one line? If so, then can’t every piece of code qualify as a one-liner, if we just remove all the newline characters? Think about it.

A minified JavaScript file with only one line of code.

It seems like we need a more rigorous definition of what qualifies as a one-liner. And, after a few minutes of thought when writing a previous article on one-liners, I came up with this:

A one-liner is a code solution to a problem, implemented with a single statement in a particular programming language, optionally using only first-party utilities.

Tari Ibaba (😎)

You can clearly see those particular keywords that set this definition apart from others

1. “…single statement…”

Single line or single statement? I go with the later.

Because the thing is, we squeeze every program ever made in a single line of code if we wanted; all the whitespace and file separation is only for us and our fellow developers.

If you’ve used Uglify.js or a similar minifier, you know what it does to all those pitiful lines of code; why it’s called Uglify.

Uglify changes this:

/** Obviously redudant comments here. Just meant to emphasize what Uglify does Class Person */ class Person { /** Constructor */ constructor(name, age) { this.name = name; this.age = age; } /** Print Message */ printMessage() { console.log(`Hello! My name is ${this.name} and I am ${this.age} years old.`); } } /** Creating Object */ var person = new Person('John Doe', 25); /** Printing Message */ person.printMessage();

to this:

class Person{constructor(e,n){this.name=e,this.age=n}printMessage(){console.log(`Hello! My name is ${this.name} and I am ${this.age} years old.`)}}var person=new Person("John Doe",25);person.printMessage();

Would you be impressed by someone who actually wrote code like in this minified way? I would say it’s just badly formatted code.

Would you call this a one-liner?

const sum = (a, b) => { const s1 = a * a; const s2 = b * b; return s1 + s2; }

Tools like the VS Code Prettier extension will easily split up those 3 statements into multiple lines:

The three statements are separated after VS Code Prettier auto-format on save.
The three statements are separated after VS Code Prettier auto-format on save.

A true one-liner way to get the sum of two squares would be something like this:

const sum = (a, b) => a * a + b * b;

One short, succinct statement does the same job with equal clarity.

On the other hand, what about code that spans multiple lines but only uses one statement? Like this:

const capitalizeWithoutSpaces = (str) => str .split('') .filter((char) => char.trim()) .map((char) => char.toUpperCase()) .join('');

I would say this function’s body is far more qualified to be a one-line than the two examples we saw above; single statement.

“…particular programming language”

We need this part because of abstraction.

int sum(int a, int b) { return a * a + b * b; }

This is a one-liner, isn’t it? Very harmless-looking and easy to understand.

How about now? :

sum(int, int): push rbp mov rbp, rsp mov DWORD PTR [rbp-4], edi mov DWORD PTR [rbp-8], esi mov eax, DWORD PTR [rbp-4] imul eax, eax mov edx, eax mov eax, DWORD PTR [rbp-8] imul eax, eax add eax, edx pop rbp ret

Wait, this is just another piece of code, right? Well, it is, except that they do exactly the same thing, but in different languages. One in C++, and the other in Assembly.

Now imagine how much more an equivalent machine language program will have. Clearly, we can only our sum() a one-liner function in the context of C++.

“…using only first-party utilities”

Once again, we need this part because of abstraction.

For us to consider a piece of code as a one-liner, it should only use built-in functions and methods that are part of the language’s standard library or core functionality. For example: array methods, the http module in Node.js, the os module in Python, and so on.

Without this, capitalizeWithoutSpaces() below would easily pass as a JavaScript one-liner:

// Not a one-liner const capitalizeWithoutSpaces = (str) => filter(str.split(''), (char) => char.trim()) .map((char) => char.toUpperCase()) .join(''); function filter(arr, callback) { // Look at all these lines const result = []; for (const item of arr) { if (callback(item)) { result.push(item); } } return result; }

filter could have contained many thousands of lines, yet capitalizeWithoutSpaces would still be given one-liner status.

It’s kind of controversial because a lot of these so-called first-party utilities are abstractions themselves with logic spanning dozens of lines. But just like the “single statement” specifier, it makes it impossible to have an unlimited number of one-liners.

Final thoughts

The essence of a one-liner in programming extends beyond the literal interpretation of its name. It lies not only in the minimalism of physical lines but also in the elegance and sophistication of its execution. It often requires a sound comprehension of the language at hand, an ability to concisely solve a problem, and the art of utilizing the language’s core functionalities with finesse.

A one-liner isn’t merely about squeezing code into a single line; It is where the clarity of thought, the elegance of language mastery, and the succinctness of execution converge. It’s the realm where brevity meets brilliance and the art of coding truly shines.

Mojo: 7 brilliant Python upgrades in the new AI language

It is 35,000 times faster than Python. It is quicker than C. It is as easy as Python.

Enter Mojo: a newly released programming language made for AI developers and made by Modular, a company founded by Chris Lattner, the original creator of Swift.

This 35000x claim came from a benchmark comparison between Mojo and other languages, using the Mandelbrot algorithm on a particular AWS instance.
This 35000x claim came from a benchmark comparison between Mojo and other languages, using the Mandelbrot algorithm on a particular AWS instance.

It’s a superset of Python, combining Python’s usability, simplicity, and versatility with C’s incredible performance.

If you’re passionate about AI and already have a grasp on Python, then Mojo is definitely worth a try. So, let’s dive in and explore 7 powerful features of this exciting language together.

Mojo’s features

I signed up for Mojo access shortly after it was announced and got access a few days later.

I got access to the Mojo playground.

I started exploring all the cool new features they had to offer and even had the chance to run some code and see the language in action. Here are 7 interesting Python upgrades I found:

1. let and var declarations

Mojo introduces new let and var statements that let us create variables.

If we like we can specify a type like Int or String for the variable, as we do in TypeScript. var allows variables to change; let doesn’t. So it’s not like JavaScript’s let and var – There’s no hoisting for var and let is constant.

def your_function(a, b): let c = a # Uncomment to see an error: # c = b # error: c is immutable if c != b: let d = b print(d) your_function(2, 3)

2. structs for faster abstraction

We have them in C++, Go, and more.

Structs are a Mojo feature similar to Python classes, but they’re different because Mojo classes are static: you can’t add more methods are runtime. This is a trade-off, as it’s less flexible, but faster.

struct MyPair: var first: Int var second: Int # We use 'fn' instead of 'def' here - we'll explain that soon fn __init__(inout self, first: Int, second: Int): self.first = first self.second = second fn __lt__(self, rhs: MyPair) -> Bool: return self.first < rhs.first or (self.first == rhs.first and self.second < rhs.second)

Here’s one way struct is stricter than class: all fields must be explicitly defined:

Fields must be explicitly defined in Mojo structs.

3. Strong type checking

These structs don’t just give us flexibility, they let us check variable types at compile-time in Mojo, like the TypeScript compiler does.

def pairTest() -> Bool: let p = MyPair(1, 2) # Uncomment to see an error: # return p < 4 # gives a compile time error return True

The 4 is an Int, the p is a MyPair; Mojo simply can’t allow this comparison.

4. Method overloading

C++, Java, Swift, etc. have these.

Function overloading is when there are multiple functions with the same name that accept parameters with different data types.

Look at this:

struct Complex: var re: F32 var im: F32 fn __init__(inout self, x: F32): """Makes a complex number from a real number.""" self.re = x self.im = 0.0 fn __init__(inout self, r: F32, i: F32): """Makes a complex number from its real and imaginary parts.""" self.re = r self.im = i

Typeless languages like JavaScript and Python simply can’t have function overloads, for obvious reasons.

Although overloading is allowed in module/file functions and class methods based on parameter/type, it won’t work based on return type alone, and your function arguments need to have types. If don’t do this, overloading won’t work; all that’ll happen is the most recently defined function will overwrite all those previously defined functions with the same name.

5. Easy integration with Python modules

Having seamless Python support is Mojo’s biggest selling point by far.

And using Python modules in Mojo is straightforward. As a superset, all you need to do is call the Python.import_module() method, with the module name.

Here I’m importing numpy, one of the most popular Python libraries in the world.

from PythonInterface import Python # Think of this as `import numpy as np` in Python let np = Python.import_module("numpy") # Now it's like you're using numpy in Python array = np.array([1, 2, 3]) print(array)

You can do the same for any Python module; the one limitation is that you have to import the whole module to access individual members.

All the Python modules will run 35,000 times faster in Mojo.

6. fn definitions

fn is basically def with stricter rules.

def is flexible, mutable, Python-friendly; fn is constant, stable, and Python-enriching. It’s like JavaScript’s strict mode, but just for def.

struct MyPair: fn __init__(inout self, first: Int, second: Int): self.first = first self.second = second

fn‘s rules:

  • Immutable arguments: Arguments are immutable by default – including self – so you can’t mistakenly mutate them.
  • Required argument types: You have to specify types for its arguments.
  • Required variable declarations: You must declare local variables in the fn before using them (with let and var of course).
  • Explicit exception declaration: If the fn throws exceptions, you must explicitly indicate so – like we do in Java with the throws keyword.

7. Mutable and immutable function arguments

Pass-by-value vs pass-by-reference.

You may have across this concept in languages like C++.

Python’s def function uses pass-by-reference, just like in JavaScript; you can mutate objects passed as arguments inside the def. But Mojo’s def uses pass-by-value, so what you get inside a def is a copy of the passed object. So you can mutate that copy all you want; the changes won’t affect the main object.

Pass-by-reference improves memory efficiency as we don’t have to make a copy of the object for the function.

But what about the new fn function? Like Python’s def, it uses pass-by-reference by default, but a key difference is that those references are immutable. So we can read the original object in the function, but we can’t mutate it.

Immutable arguments

borrowed a fresh, new, redundant keyword in Mojo.

Because what borrowed does is to make arguments in a Mojo fn function immutable – which they are by default. This is invaluable when dealing with objects that take up a substantial amount of memory, or we’re not allowed to make a copy of the object we’re passing.

For example:

fn use_something_big(borrowed a: SomethingBig, b: SomethingBig): """'a' and 'b' are both immutable, because 'borrowed' is the default.""" a.print_id() // 10 b.print_id() // 20 let a = SomethingBig(10) let b = SomethingBig(20) use_something_big(a, b)

Instead of making a copy of the huge SomethingBig object in the fn function, we simply pass a reference as an immutable argument.

Mutable arguments

If we want mutable arguments instead, we’ll use the new inout keyword instead:

struct Car: var id_number: Int var color: String fn __init__(inout self, id: Int): self.id_number = id self.color = 'none' # self is passed by-reference for mutation as described above. fn set_color(inout self, color: String): self.color = color # Arguments like self are passed as borrowed by default. fn print_id(self): # Same as: fn print_id(borrowed self): print('Id: {0}, color: {1}') car = Car(11) car.set_color('red') # No error

self is immutable in fn functions, so we here we needed inout to modify the color field in set_color.

Key takeaways

  • Mojo: is a new AI programming language that has the speed of C, and the simplicity of Python.
  • let and var declarations: Mojo introduces let and var statements for creating optionally typed variables. var variables are mutable, let variables are not.
  • Structs: Mojo features static structs, similar to Python classes but faster due to their immutability.
  • Strong type checking: Mojo supports compile-time type checking, akin to TypeScript.
  • Method overloading: Mojo allows function overloading, where functions with the same name can accept different data types.
  • Python module integration: Mojo offers seamless Python support, running Python modules significantly faster.
  • fn definitions: The fn keyword in Mojo is a stricter version of Python’s def, requiring immutable arguments and explicit exception declaration.
  • Mutable and immutable arguments: Mojo introduces mutable (inout) and immutable (borrowed) function arguments.

Final thoughts

As we witness the unveiling of Mojo, it’s intriguing to think how this new AI-focused language might revolutionize the programming realm. Bridging the performance gap with the ease-of-use Python offers, and introducing powerful features like strong type checking, might herald a new era in AI development. Let’s embrace this shift with curiosity and eagerness to exploit the full potential of Mojo.

Why “Yarn 2” is actually Yarn 3

What do we know as Yarn 2?

It’s the modern version of Yarn that comes with important upgrades to the package manager including PNMP-style symlinks, and an innovative new Plug ‘n’ Play module installation method for much-reduced project sizes and rapid installations.

But after migrating from Yarn 1, you’ll find something interesting, as I did – the thing widely known as Yarn 2 is actually… version 3?

After migrating to "Yarn 2" and checking the version, it was shown to be version 3.

Why is “Yarn 2” using version 3?

It’s because Yarn 1 served as the initial codebase which was completely overhauled in the Yarn v2.0 (the actual version 2), enhancing its efficiency and effectiveness, with its launch taking place in January 2020. As time moved on, the introduction of a fresh major, Yarn v3.0, happened, thankfully without the need for another codebase rewrite. The upcoming major update is expected to be Yarn v4.0, and so on.

Despite the historical tendency of releasing few major updates, there was a growing trend among some individuals to label everything that used the new codebase as “Yarn 2”, which includes Yarn 2.x versions and future ones such as 3.x. This, however, was a misinterpretation as “Yarn 2” strictly refers to the 2.x versions. A more accurate way to reference the new codebase would be “Yarn 2+” or “Yarn Berry” – a codename that the team selected for the new codebase when they started developing it.

As once stated by one of the maintainers in a related GitHub discussion:

Some people have started to colloquially call “Yarn 2” everything using this new codebase, so Yarn 2.x and beyond (including 3.x). This is incorrect though (“Yarn 2” is really just 2.x), and a better term to refer to the new codebase would be Yarn 2+, or Yarn Berry (which is the codename I picked for the new codebase when I started working on it).

arcanis, a Yarn maintainer

How to migrate from Yarn v1 to Yarn Berry

A Yarn Berry installation in progress.
A Yarn Berry installation in progress.

If you’re still using Yarn version 1 – or worse, NPM – you’re missing out.

The new Yarn is loaded with a sizable number of upgrades that will significantly improve your developer experience when you start using it. These range from notable improvements in stability, flexibility, and extensibility, to brand new features, like Constraints.

You can migrate from Yarn v1 to Yarn Berry in 7 easy steps:

  1. Make sure you’re using Node version 18+.
  2. Run corepack enable to activate Corepack.
  3. Navigate to your project directory.
  4. Run yarn set version berry.
  5. Convert your .npmrc and .yarnrc files into .yarnrc.yml (as explained here).
  6. Run yarn install to migrate the lockfile.
  7. Commit all changes.

In case you experience any issues due to breaking changes, this official Yarn Berry migration guide should help.

Final thoughts

The Yarn versioning saga teaches us an important lesson: terminology matters.

What many of us dub as “Yarn 2” is actually “Yarn 2+” or “Yarn Berry”, the game-changing codebase. This misnomer emphasizes our need to stay current, not just with evolving tools and features, but with their rightful names as well. After all, how we understand and converse about these improvements shapes our effectiveness and fluency as developers.

Fine-tuning for OpenAI’s GPT-3.5 Turbo model is finally here

Some great news lately for AI developers from OpenAI.

Finally, you can now fine-tune the GPT-3.5 Turbo model using your own data. This gives you the ability to create customized versions of the OpenAI model that perform incredibly well at specific tasks and give responses in a customized format and tone, perfect for your use case.

For example, we can use fine-tuning to ensure that our model always responds in a JSON format, containing Spanish, with a friendly, informal tone. Or we could make a model that only gives one out of a finite set of responses, e.g., rating customer reviews as critical, positive, or neutral, according to how *we* define these terms.

As stated by OpenAI, early testers have successfully used fine-tuning in various areas, such as being able to:

  • Make the model output results in a more consistent and reliable format.
  • Match a specific brand’s style and messaging.
  • Improve how well the model follows instructions.

The company also claims that fine-tuned GPT-3.5 Turbo models can match and even exceed the capabilities of base GPT-4 for certain tasks.

Before now, fine-tuning was only possible with weaker, costlier GPT-3 models, like davinci-002 and babbage-002. Providing custom data for a GPT-3.5 Turbo model was only possible with techniques like few-shot prompting and vector embedding.

OpenAI also assures that any data used for fine-tuning any of their models belongs to the customer, and then don’t use it to train their models.

What is GPT-3.5 Turbo, anyway?

Launched earlier this year, GPT-3.5 Turbo is a model range that OpenAI introduced, stating that it is perfect for applications that do not solely focus on chat. It boasts the capability to manage 4,000 tokens at once, a figure that is twice the capacity of the preceding model. The company highlighted that preliminary users successfully shortened their prompts by 90% after applying fine-tuning on the GPT-3.5 Turbo model.

What can I use GPT-3.5 Turbo fine-tuning for?

  • Customer service automation: We can use a fine-tuned GPT model to make virtual customer service agents or chatbots that deliver responses in line with the brand’s tone and messaging.
  • Content generation: The model can be used for generating marketing content, blog posts, or social media posts. The fine-tuning would allow the model to generate content in a brand-specific style according to prompts given.
  • Code generation & auto-completion: In software development, such a model can provide developers with code suggestions and autocompletion to boost their productivity and get coding done faster.
  • Translation: We can use a fine-tuned GPT model for translation tasks, converting text from one language to another with greater precision. For example, the model can be tuned to follow specific grammatical and syntactical rules of different languages, which can lead to higher accuracy translations.
  • Text summarization: We can apply the model in summarizing lengthy texts such as articles, reports, or books. After fine-tuning, it can consistently output summaries that capture the key points and ideas without distorting the original meaning. This could be particularly useful for educational platforms, news services, or any scenario where digesting large amounts of information quickly is crucial.

How much will GPT-3.5 Turbo fine-tuning cost?

There’s the cost of fine-tuning and then the actual usage cost.

  • Training: $0.008 / 1K tokens
  • Usage input: $0.012 / 1K tokens
  • Usage output: $0.016 / 1K tokens

For example, a gpt-3.5-turbo fine-tuning job with a training file of 100,000 tokens that is trained for 3 epochs would have an expected cost of $2.40.

OpenAI, GPT 3.5 Turbo fine-tuning and API updates

When will fine-tuning for GPT-4 be available?

This fall.

OpenAI has announced that support for fine-tuning GPT-4, its most recent version of the large language model, is expected to be available later this year, probably during the fall season. This upgraded model has been proven to perform at par with humans across diverse professional and academic benchmarks. It surpasses GPT-3.5 in terms of reliability, creativity, and its capacity to deal with instructions that are more nuanced.

10 powerful JavaScript animation libraries for engaging user experiences

Animations. A fantastic way to stand out from the crowd and grab the attention of your visitors.

With creative object motion and fluid page transitions, you not only add a unique aesthetic appeal to your website but also enhance user engagement and create a memorable first impression.

And creating animations can’t get any easier with these 10 powerful JavaScript libraries. Scroll animations, handwriting animations, SPA page transitions, typing animations, color animations, SVG animations… they are endlessly capable. They are the best.

1. Anime.js

An animation creating with Anime.js
An animation created with Anime.js.

With over 43k stars on GitHub, Anime.js is easily one of the most popular animation libraries out there.

It’s a lightweight JavaScript animation library with a simple API that can be used to animate CSS properties, SVG, DOM attributes, and JavaScript objects. With Anime.js, you can play, pause, restart or reverse an animation. The library also provides staggering features for animating multiple elements with follow-through and overlapping actions. There are various animation-related events also included, which we can listen to using callbacks and Promises.

Visit the Anime.js website

2. Lottie

An animation created with Lottie.js
An animation created with Lottie.

Lottie is a library that parses Adobe After Effects animations exported as JSON with the Bodymovin plugin and renders them natively on mobile and web applications. This eliminates the need to manually recreate the advanced animations created in After Effects by expert designers. The Web version alone has over 27k stars on GitHub.

Visit the Lottie website

3. Velocity

An animation created with Velocity.
An animation created with Velocity.

With Velocity you create color animations, transforms, loops, easings, SVG animations, and more. It uses the same API as the $.animate() method from the jQuery library, and it can integrate with jQuery if it is available. The library provides fade, scroll, and slide effects. Besides being able to control the duration and delay of an animation, you can reverse it sometime after it has been completed, or stop it altogether when it is in progress. It has over 17k stars on GitHub and is a good alternative to Anime.js.

Visit the Velocity website

4. Rough Notation

Som Rough Notation annotation styles.
Some Rough Notation annotation styles.

Rough Notation is a JavaScript library for creating and animating colorful annotations on a web page. It uses RoughJS to create a hand-drawn look and feel. You can create several annotation styles, including underline, box, circle, highlight, strike-through, etc., and control the duration and color of each annotation style.

Visit the Rough Notation website

5. Popmotion

An animation created with Popmotion.
An animation created with Popmotion.

Popmotion is a functional library for creating prominent and attention-grabbing animations. What makes it stand out? – there are zero assumptions about the object properties you intend to animate, but instead provides simple, composable functions that can be used in any JavaScript environment.

The library supports keyframes, spring and inertia animations on numbers, colors, and complex strings. It is well-tested, actively maintained, and has over 19k stars on GitHub.

Visit the Popmotion website

6. Vivus

An animation created with Vivus.
An animation created with Vivus.

Vivus is a JavaScript library that allows you to animate SVGs, giving them the appearance of being drawn. It is fast and lightweight with exactly zero dependencies, and provides three different ways to animate SVGs: Delayed, Sync, and OneByOne. You can also use a custom script to draw an SVG in your preferred way.

Vivus also allows you to customize the duration, delay, timing function, and other animation settings. Check out Vivus Instant for live, hands-on examples.

Visit the Vivus website

7. GreenSock Animation Platform (GSAP)

An animation created with GSAP

The GreenSock Animation Platform (GSAP) is a library that lets you create wonderful animations that work across all major browsers. You can use it in React, Vue, WebGL, and the HTML canvas to animate colors, strings, motion paths, and more. It also comes with a ScrollTrigger plugin that lets you create impressive scroll-based animations with little code.

Used in over 11 million sites, with over 15k stars on GitHub, it is a versatile and popular indeed. You can use the GSDevTools from GreenSock to easily debug animations created with GSAP.

Visit the GSAP website

8. Three.js

An animation created with Three.js
An animation created with Three.js

Three.js is a lightweight library for displaying complex 3D objects and animations. It makes use of WebGL, SVG, and CSS3D renderers to create engaging three-dimensional experiences that work across a wide range of browsers and devices. It is a well-known library in the JavaScript community, with over 85k stars on GitHub.

Visit Three.js website

9. ScrollReveal

ScrollReveal animations.
ScrollReveal animations.

The ScrollReveal library lets you easily animate a DOM element as it enters or leaves the browser viewport. It provides various types of elegant effects to reveal or hide an element on-scroll in multiple browsers. And quite easy to use too, with with zero dependencies and over 21k stars on GitHub.

Visit the ScrollReveal website

10. Barba.js

Page transitions created with Barba.js.
Page transitions created with Barba.js.

One creative way to make your website outstanding is to add lively transitions between the pages as your users navigate between them. This produces a better user experience than simply displaying the new webpage or reloading the browser.

And that’s why Barba.js is so useful; this library lets you create enjoyable page transitions by making the site run like a Single Page Application (SPA). It reduces the delay between pages and minimizes the number of HTTP requests that the browser makes. It’s gotten almost 11k stars on GitHub.

Visit Barba.js website


11. Mo.js

An animation created with Mo.js
An animation created with Mo.js.

Great library for creating compelling motion graphics.

It provides simple, declarative APIs for effortlessly creating smooth animations and effects that look great on devices of various screen sizes. You can move HTML or SVG DOM elements, or you can create a special Mo.js object, which comes with a set of unique capabilities. It is a reliable and well-tested library, with over 1500 tests written and over 17k stars on GitHub.

Visit the Mo.js website

12. Typed.js

An animation created with Typed.js

The name says it all; an animated typing library.

It types out a specific string character by character as if someone was typing in real-time, allowing you pause the typing speed, and even pause the typing for a specific amount of time. With smart backspacing, it types out successive strings starting with the same set of characters as the current one without backspacing the entire preceding string – as we saw in the demo above.

Also included is support for bulk typing, which types out a group of characters on the screen at the same time, instead of one after the other. Typed.js has over 12k stars on GitHub and is trusted by Slack and Envato.

Visit the Typed.js website

Final thoughts

The world of web animation is vast and dynamic, constantly evolving with the advent of new technologies and libraries. The animation libraries highlighted in this article offer an array of features to create engaging, interactive, and visually appealing experiences for users. They are a testament to the power and flexibility of JavaScript, and demonstrate how animations greatly enhance the user experience.

As a developer, harnessing these tools will no doubt elevate your projects, making them stand out in an increasingly competitive digital landscape.

10 essential VS Code tips & tricks for greater productivity


Did you know that 73% of developers worldwide rely on the same code editor?

Yes, the 2023 Stack Overflow Developer Survey results are in, and yet again, Visual Studio Code was by far the most used development environment.

73% of
“Visual Studio Code remains the preferred IDE across all developers, increasing its use among those learning to code compared to professional developers”, survey.stackoverflow.co/2023

And we all know why: it’s awesome.

But are we fully exploring its potential? In this article, we unfold some compelling VS Code features that enhance productivity with local source control, animated typing, and rapid line deletion, amongst others. Let us start using them to achieve our coding goals faster than ever.

1. Timeline view: local source control

The Timeline view gives us local source control.

Many of us know how useful Git and other source control tools are, helping us easily track file changes and revert back to a previous point when needed.

So the Timeline view in VS Code provides an automatically updated timeline of important events related to a file, such as Git commits, file saves, and test runs.

The Visual Studio Code Timeline view is available by default in the Explorer pane.

Expand this view to see a list of snapshot of events related to the current file. Here it’s file saves, but also Git commits where the file was staged.

The Timeline view shows a list of snapshot of events related to the current file.

Hover over the snapshot item to view the date and time when VS Code made the snapshot.

Hover over the snapshot item to view the date and time when VS Code made the snapshot.

Select a snapshot item to see a diff view showing the changes between the file at the snapshot time and the file presently.

Select a snapshot item to see a diff view showing the changes between the file at the snapshot time and the file presently.

2. Autosave: no more Ctrl + S

Can you count how many times you’ve used this shortcut? You probably do it unconsciously now.

The Autosave feature automatically saves files as we work on them, removing the need for manual saving. With autosave, we eliminate Ctrl + S fatigue, save time, and gain certainty of always working with the latest changes to the files.

It’s not perfect though, and it’s up to you to weigh the pros and cons – which we comprehensively cover here.

Visual Studio Code without autosave.
No autosave.
Visual Studio Code with autosave.
Autosave enabled – the unsaved indicator no longer shows.

Use File > Auto Save to enable the feature easily.

File > Autosave enables autosave in VS Code.

3. Do anything with Command Palette

Almost anything you do in VS Code apart from typing is a “Command”.

Commands let us accomplish tasks within the editor, and they include file-related commands, navigation commands, editing commands, and terminal commands, each optimally designed to enhance different aspects of your editing experience.

So with Command Palette we simply search for a command and select to perform the associated action.

To open the Command Palette, use this keyboard shortcut:

  • Windows/Linux: Ctrl + Shift + P
  • Mac: Shift + Command+ P
The VS Code Command Palette.

As you guessed correctly, those keyboard shortcuts to the right are a faster way to run the commands with the keyboard.

The key benefit of the Command Palette over shortcuts is when there’s a command without a shortcut, or you’re looking for a command you’re not sure exists.

4. Go to file quickly

The mouse is too slow.

Yes, you can click on the file in the Explorer pane, but for a much faster alternative use Ctrl + P to search for and open a specific file in your project.

Use Ctrl + P to search for an open a specific file in a VS Code project.

Hold Ctrl and press Tab to cycle through the list of files currently open in an editor instance.

Hold Ctrl and press Tab to cycle through the list of files currently open in an editor instance.

You can even use Alt + Left and Alt + Right to quickly navigate between these open files.

All these are much faster ways to get to a file than using the cursor.

5. Go to line quickly

Jump, don’t scroll.

Quickly navigating to a line is invaluable during debugging when you need to encounter errors at specific line numbers. By jumping to those lines, you can examine the code in that particular context, evaluate variables, and troubleshoot the issue.

Use the Ctrl + G keyboard shortcut for this.

Use the Ctrl + G keyboard shortcut to quickly navigate to a particular line in Visual Studio Code.

6. Delete line quickly

You’ve got to the line now, what if you want to delete it?

Will you drag and drag to highlight and then press Delete? Will you tirelessly press Backspace until every character is gone?

Or, will you use the Ctrl + Shift + K shortcut to rapidly delete that and dozens more lines in a matter of seconds?

Use the Ctrl + Shift + K keyboard shortcut to quickly delete a line.

7. Enjoy typing with smooth cursor

VS Code has this smooth cursor feature that animates the cursor as it moves, like in MS Word. This makes typing feel more fluid and polished, as well as giving us a smoother and more natural feel as we navigate through the lines of code and place the cursor at different points.

Smooth scrolling in Visual Studio Code.

To turn it on, opens Settings UI in the Command Palette and search for “smoot caret”.

We are looking for Editor: Cursor Smooth Caret Animation setting, which has 3 possible options:

The "Editor: Cursor Smooth Caret Animation" setting enables smooth caret animation in Visual Studio Code.
  1. off: No smooth cursor animation
  2. explicit: Only animates the cursor when we explicitly place it somewhere in the code.
  3. on: Smooth cursor animation is always enabled – including when typing.

Set it to on to get the full visual experience.

8. Format code rapidly

Formatting is all about improving code readability by organizing it in a structured and consistent manner.

And if you’ve been doing this manually, you need to know that there’s a better way.

Yes, you need to start formatting code automatically with the Format Document command, easily accessible in the Command Palette. Depending on the current file’s language, a particular “default” formatter will be used to format the code using various rules of indentation, line length, braces and brackets, etc.

The "Format Document" command in the VS Code Command Palette.

While there’s a pretty decent built-in JS/TS formatter, for a more robust solution, I highly recommend the Prettier extension.

Format On Save in action in Visual Studio Code.
The Prettier formatter for VS Code.

After installing, you’ll set it as your default formatter.

When you use manual over autosave, there’s a feature you should enable to make formatting a bit easier:

Enabling the "Editor: Format On Save" setting in VS Code.
  • Editor: Format On Save: “Format a file on save. A formatter must be available, the file must not be saved after delay, and the editor must be shutting down”. It is disabled by default.

So with this setting on, VS Code will automatically format your code with the current default formatter when you save the file with Ctrl + S, as you saw in the above demo.

Format On Save in action in Visual Studio Code.

When you do autosave, it can get tedious to continuously open the Command Palette when you’re formatting every now and then. And that’s what keyboard shortcuts are for:

  • Windows: Shift + Alt + F
  • Mac: Shift + Option + F
  • Linux: Ctrl + Shift + I

I’m on Windows and personally, I don’t like this default keyboard shortcut; autosave makes me format every now and then, and Shift + Alt + F got agonizing after a while.

So I changed it to Ctrl + D, Ctrl + D – a keyboard shortcut chord that’s much easier to press and remember, and has no conflicting keybinding. I recommend you do the same.

9. Save time with multi-cursor editing

One of the wow moments in my earliest VS Code days, the multi-cursor editing lets you place multiple cursors at different points, and delete or insert the same text multiple times. This speeds up editing time and boosts productivity greatly, as we get repetitive tasks done efficiently with rapid code creation.

Of course, when editing, there’s always at least one cursor. Use Alt + Click to add more.

Add multiple cursors with Alt + Click in VS Code.

You can also easily add a cursor directly above or below the current line, with Ctrl + Alt + Down or Ctrl + Alt + Up.

Add a cursor below in Visual Studio Code with the Ctrl + Alt + Down keyboard shortcut.

These shortcuts call the Add Cursor Below and Add Cursor Above commands respectively.

10. Create new folder / file quickly

There’s no serious project where we don’t create new folders and files, and if there was a way to accelerate file/folder creation, all the time saved would add up to give us a significant productivity enhancement.

If you’ve been creating new files and folders in VS Code with the new file and new folder button, then yes, there is a way.

Creating a new file/folder in VS Code with the buttons take time.
Yeah, don’t do this.

Instead of constantly moving your mouse to locate those small buttons, did you know you can just double-click on the Explorer panel to create a new file?

Double-clicking to create a new file in Visual Studio Code.

How about a new folder? Well, folders are nothing without files, and when you’re creating a new file, you can easily use the / character to indicate a hierarchy and create new folders and sub-folders to contain that file.

Using the forward-slash (/) to indicate a hierarchy and create new folders and sub-folders to contain that file
The utils folder is created to contain index.js.

It would be even more efficient to use keyboard shortcuts, which is what I did.

As a former Atom fan, I had quickly gotten used to the A and Shift + A shortcuts for creating new files and folder respectively; I knew what I had to do.

Creating a new file/folder in VS Code with keyboard shortcuts.
Create shortcuts to create a new file/folder in VS Code.

Since A and Shift + A are obviously keys used to code, I included the when values here to make sure they only create a new file/folder when the Explorer pane has focus and there’s no active cursor in the current editor.

So to use these shortcuts when typing, you’ll have to focus on the explorer pane first; click on it, or use Ctrl/Command + Shift + E.

Key takeaways

  • Enable local source control with Timeline view; available in Explorer pane by default.
  • Autosave files with File > Autosave.
  • Run commands in Command Palette with Ctrl + Shift + P or Shift + Command + P.
  • Go to a file with Ctrl + P, navigate between open files with Alt + Left/Right or Ctrl + Tab.
  • Go to a line with Ctrl + G.
  • Delete a line with Ctrl + Shift + K
  • Enable smooth typing with Editor: Cursor Smooth Caret Animation setting.
  • Format code with Format Document command, use Prettier, change shortcut to Ctrl + D, Ctrl + D
  • Use multiple cursors at once with Alt + Click, Ctrl + Alt + Up/Down adds one above/below
  • Move a line up or down with Alt/Option + Up/Down in Windows/Mac
  • Create a new file by double-clicking the Explorer pane or set a custom keyboard shortcut. Create a new file in a new folder with “folder/file.ext

Check out the VS Code Key Bindings docs to learn more about keyboard shortcuts and how to customize them. It includes a complete list of all the default VS Code shortcuts in your operating system.

Final thoughts

Visual Studio Code is more than just a text editor—it’s a powerful tool that, when mastered, can significantly boost your productivity and streamline your coding workflow. The ten tips and tricks we’ve explored are just the tip of the iceberg. As you continue to navigate through VS Code, you will discover a myriad of other features and shortcuts that will further enhance your coding experience. So, keep exploring, keep learning, and remember: the key to efficient coding lies not just in the code itself, but also in the tools you use to write it.

This new ES7 feature made my math 3 times easier

But 5 lines of Java is one line of Python.

How many times have you heard something like that from lovers of the later?

Seems like they love to trash languages they stubbornly believe are verbose. I came to see that “Pythonic” is something truly cherished by our friends in the Python community.

Your Python code works, and so what? Where is elegance? Where is readability?

Think you can write a simple for loop and get away with it?

total = 0 for i in range(1, 11): total += i print("The sum of the first 10 numbers is:", total)

Just wait till one of them find out — to say you’ll face severe criticism is an understatement.

Because apparently — and I kind of agree — it’s just not “beautiful” or concise enough.

To be “Pythonic” is best.

total = sum(i for i in range(1, 11)) print("The sum of the first 10 numbers is:", total)

An ES7 feature that brings syntactic sugar and conciseness

The ** operator.

This one almost always comes up in Python’s favor when talking about language conciseness, up there with generators and the // operator.

It’s good to know JavaScript now has this feature, over 6 years ago in fact.

But it was surprising to know that a sizeable number of our fellow JavaScripters never knew it’s in the language.

It’s now effortless to get the power of a number, with the ** operator. Instead of Math.pow(a, b), you do a ** b.

const result = Math.pow(10, 2); console.log(result); // 100 const result2 = Math.pow(2, Math.pow(3, 2)); console.log(result2); const result3 = 10 ** 2; console.log(result3); // 100 const result4 = 2 ** 3 ** 2; console.log(result4) // 512

We don’t need a function for such a common math operation anymore.

You can even pass a decimal number as a power with **Math.pow() can do this too:

const result = Math.pow(49, 1.5); console.log(result); // 343 const result2 = 49 ** 1.5; console.log(result2); // 343

And it’s not only a drop-in replacement for Math.pow(); ** can take BigInts too:

// ❌ Error: Cannot convert a BigInt value to a number const result1 = Math.pow(32n, 2); console.log(result1);
const result2 = 32n ** 2n; console.log(result2); // 1024n

BigInts let us represent numbers of any size without losing precision or experiencing overflow errors.

const veryLargeNumber = 1234567890123456789012345678901234567890n; console.log(typeof veryLargeNumber); // "bigint" console.log(veryLargeNumber * 2n); // 2469135780246913578024691357802469135780n

You can see that we simply add an n at the end of the digits to make it a BigInt.

Final thoughts

Language wars are a fun programmer pastime.

It’s always fun to debate about which programming language is more elegant and concise.

But at the end of the day, we’ve got to keep in mind that writing readable and maintainable code is what matters most.

In this article, we saw that the ** operator introduced in ES7 for JavaScript is a neat trick that can make your code more concise, and it even works with BigInts!

More features keep getting added every year — ES13 was released in 2022 — to increase and add more syntactic sugar.

So, keep exploring the possibilities of your favorite programming language, and have fun coding!

Unleashing the power of JavaScript generators: from basics to real-world applications (complete guide)

Let’s talk about generators…

Generators, you ask?

Well, they’re these special functions in JavaScript…

They give you more control over the function execution process.

It’s like having a magic wand for your code!

You might not have used them much, or at all.

Perhaps you’ve been coding without realizing their potential.

Or maybe they seemed a little intimidating or unnecessary at first glance.

No worries, we’ve all been there.

But once you unlock the magic of generators, there’s no turning back.

You’ll find them incredibly useful in a variety of coding scenarios.

So, are you ready to journey into the world of JavaScript generators?

Let’s dive right in!

Deep dive into generators

To kickstart our journey let’s look at what’s so unique about generators.

Well unlike regular functions generators pause and resume their execution.

Intriguing isn’t it?

Let’s look at a quick example:

function* myGenerator() { yield 'Hello'; yield 'World'; } const iterator = myGenerator(); console.log(iterator.next()); // {value: 'Hello', done: false} console.log(iterator.next()); // {value: 'World', done: false} console.log(iterator.next()); // {value: undefined, done: true}

This example paints a clear picture of what’s happening.

Notice how the keyword function* is used to define a generator.

That * marks our function as a generator.

You can also see the yield keyword.

yield is how we pause execution in a generator.

Each time yield is encountered, the generator gives a value back.

This value can be accessed using the .next() method.

.next() is a powerful method provided by generators.

In the example, iterator.next() is called three times.

The first two calls to next() return 'Hello' and 'World' respectively.

On the third call, the generator indicates it’s done and returns undefined.

It’s as if the generator is saying: “That’s it, friend. I have nothing more to give.”

By now, you might be realizing that generators are pretty cool.

Indeed, they are!

In the next section, we’ll dig into why they are so beneficial.

Benefits of using generators

Lazy evaluation

Lazy evaluation is a core benefit of generators.

Simply put, it means “calculate only when necessary.”

In contrast, regular functions in JavaScript execute entirely and return the result.

Let’s say you want a sequence of numbers, but you’re not sure how many. Here’s how a generator helps:

function* numberGenerator() { let number = 1; while (true) { yield number++; } } const numbers = numberGenerator(); console.log(numbers.next().value); // 1 console.log(numbers.next().value); // 2 // you can continue this as long as you need

With generators, you get the next number only when you ask for it.

Better memory utilization

Generators don’t hold all the results in memory, they generate them on the fly.

Imagine you need a sequence of a million numbers.

With a regular function, you’d need to store all these numbers in an array, using up significant memory.

A generator is more efficient:

function* bigNumberGenerator() { let number = 1; while (number <= 1000000) { yield number++; } } const bigNumbers = bigNumberGenerator(); // Generate numbers as you need them, no need to store a million numbers in memory!

Handling asynchronous tasks more efficiently

Asynchronous code can get messy.

Promises and async/await help, but generators add another tool to our toolbox.

They can pause and resume, perfect for handling asynchronous operations.

function* fetchUser(userId) { const response = yield fetch(`https://api.example.com/users/${userId}`); const user = yield response.json(); return user; }

In this code, the fetchUser generator fetches a user from an API.

It doesn’t move to the next yield until it has the result, keeping your async code clean and readable.

Real-world application: simulating a typing animation

Isn’t it mesmerizing to see typing animations on websites? They hold our attention and enhance the user experience.

Now, creating a typing animation has its challenges.

Traditional ways can be complex and a bit convoluted.

You’ve probably seen code like this:

let text = 'Hello, world!'; let index = 0; function typeAnimation() { if (index < text.length) { document.body.innerHTML += text.charAt(index); index++; setTimeout(typeAnimation, 500); } } typeAnimation();

The above function creates a typing effect, but it’s not very flexible.

You can’t easily control when it starts and stops or when it resumes.

Here’s where generators can simplify things for us.

Yes, generators, those special functions in JavaScript that can pause and resume their execution!

The beauty of generators is their ability to produce data on demand.

They don’t compute all values upfront.

Instead, they generate them when needed.

Let’s see how we can use a generator to make our typing animation.

We’ll create a generator function that yields a new character from our string each time it’s called.

Something like this:

function* typeAnimation(text) { for(let char of text) { yield char; } }

Imagine the possibilities with this generator function!

You now have more control over the typing animation.

In the next section, we’ll see how to put this generator to work.

We’ll step by step build our typing animation, using the power of generators.

Step-by-step guide to creating a typing animation using generators


Create a new HTML file. Here’s the base layout:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Typing Animation</title> </head> <body> <div id="type-here"></div> <script src="index.js"></script> </body> </html>

Notice the type-here div? That’s where our animation goes.

Also, we’ve linked to index.js. That’s our JavaScript file.

Defining the generator

JavaScript time.

Let’s create a generator function. Name it typeGenerator.

function* typeGenerator(text) { for(let char of text) { yield char; } }

What’s happening here?

Our generator goes through each character in the text.

Then, it “yields” the character.

Easy, right?

Implementing the animation

Let’s animate.

Create a generator instance with your message.

const generatorInstance = typeGenerator("Coding Beauty");

Then, initiate a typing effect function. It will use our generator instance.

function startTypingEffect(elementId, generatorInstance) { let element = document.getElementById(elementId); setInterval(() => { let result = generatorInstance.next(); if (!result.done) { element.innerHTML += result.value; } }, 100); }

And call the function:

startTypingEffect("type-here", generatorInstance);
A typing effect created using JavaScript generators.


Now, let’s explore more applications.

Further applications of generators

Asynchronous handling

Generators are pretty good at handling asynchronous tasks.

Running some action while allowing other code to run…

With generators, you can do this in a simpler way.

Here’s a code example:

function* asyncGenerator() { yield new Promise(resolve => setTimeout(() => resolve('Task 1 completed'), 2000)); yield new Promise(resolve => setTimeout(() => resolve('Task 2 completed'), 3000)); }

And here’s how we can use this generator:

const asyncGen = asyncGenerator(); asyncGen.next().value.then(console.log); // Isn't this neat? asyncGen.next().value.then(console.log);

Data streaming

Next up, generators are very helpful for data streaming.

Say you’re working with large datasets.

Often, you don’t need to process all the data at once.

With generators, you can handle data bit by bit.

This is called ‘lazy evaluation’.

Here’s a generator that does this:

function* dataStreamer(data) { for (let item of data) { yield item; } }

Let’s see how we can consume this generator:

const largeData = Array(1000000).fill().map((_, i) => i); const streamer = dataStreamer(largeData); for(let value of streamer) { // process value, it's as simple as that! }


Did you know that generators have a big role in redux-saga?

redux-saga is a library for managing side effects in applications.

In redux-saga, actions are handled by generators.

This makes testing and error handling easier.

Take a look at this simple saga:

import { call, put, takeEvery } from 'redux-saga/effects'; function* fetchData(action) { try { const data = yield call(Api.fetchUser, action.payload.userId); yield put({type: "USER_FETCH_SUCCEEDED", data}); } catch (e) { yield put({type: "USER_FETCH_FAILED", message: e.message}); } } function* mySaga() { yield takeEvery("USER_FETCH_REQUESTED", fetchData); }

In redux-saga, the saga middleware runs the generators when actions are dispatched:

import createSagaMiddleware from 'redux-saga'; import { createStore, applyMiddleware } from 'redux'; import mySaga from './sagas'; const sagaMiddleware = createSagaMiddleware(); const store = createStore( reducer, applyMiddleware(sagaMiddleware) ); sagaMiddleware.run(mySaga); // And it's done!

In summary, generators make asynchronous operations in JavaScript simpler.

They can help optimize memory use when dealing with data streaming.

And in redux-saga, they help manage side effects in an efficient way.

So, why not try using generators in your next project?

Limitations and precautions when using generators

Caution ahead!

Yes, generators are powerful.

But they’re not omnipotent.

They have their limitations.

For instance, they don’t work well with ‘return’ statements.

Consider this code:

function* myGenerator() { yield 'Hello'; return 'End'; yield 'This will not be executed'; }

Calling next() on myGenerator stops at ‘End’.

The third yield statement isn’t reached.

Now you know why!

Generator pitfalls

It’s easy to get caught in a forever loop.

For example:

function* infiniteGenerator() { let index = 0; while (true) { yield index++; } }

Use infiniteGenerator with caution. Or your code could run indefinitely!

Beware of Promises…

Generators and asynchronous operations can get tricky.

For example:

function* asyncGenerator() { const response = yield fetch('https://api.example.com/data'); return response.json(); }

This code won’t work as expected.

Generators don’t implicitly handle Promises.

But don’t worry! Libraries like co can help.

They let generators and Promises play nicely.

Embrace these cautions and become a master of generators.

Final thoughts


Generators, unique.

Yield, powerful.

No more pulling everything at once, just piecemeal.

Need more data? .next() is your friend.


function* typingGenerator(str) { for(let char of str) { yield char; } }

Easy, elegant.

It’s JavaScript’s brilliance.

Generator’s versatility? Undeniable. We’ve only scraped the surface.

Expand your horizons.

Asynchronous tasks? Data streaming? Side effects management in Redux-Saga?

Yes, generators work.

But remember, limitations exist. They don’t fit everywhere. Be cautious.

Explore more. Experiment. Push your code’s boundaries. Unleash Generators’ power.

Keep learning.

Onwards to more JavaScript adventures!

Switch from Create React App to these new tools

CRA is gone.

Yes, it’s no longer the recommended tool for starting React apps.

Some of us were surprised, but many of us knew it was a long time coming.

The React team has decided to shift their focus to more modern and lightweight tools and frameworks.

CRA was a great starting point for many, but there are now better options available.

Don’t worry though, this change is actually a positive one, as it means we can now take advantage of newer and more efficient tools that will help us build better React applications.

So, let’s embrace the change and explore these new options together!

Why did they dump CRA?

Create React App was great.

Setting up React apps become as easy as ever.

There was no need to bother with tedious Webpack or Babel configs.

But despite all that, it had notable issues we just couldn’t ignore:

1. Bulky and slow

Create React App is a bloated tool.

It installs an unbelievable number of packages and takes forever to finish.

If you’re a long-time CRA user like me, you’ve probably gotten used to this process, but it’s far from normal.

Once you enter the command to start a new project, you will quickly realize the insane number of packages CRA needs to set up.

Look at how large the node_modules folder is for CRA compared to a superior alternative!

And both folders were just newly set up.

Create React App has almost 3 times as much dependencies as Vite.

Many packages mean longer install times. Yarn and PNPM can still be slow.

2. Insecure and outdated packages

Bundling so many packages makes tracking harder. Some become insecure later.

Things improved but any insecure package is something to worry about. There are still quite a few.

Create React App has 6 high severity vulnerabilities.
6 high-severity vulnerabilities! And this is even better than it used to be.

GitHub knows this very well.

Dependabot alerts everywhere.

I still get Dependabot alerts from one of my first GitHub projects.

And they’re always about security flaws in packages CRA installed.

Security weaknesses found in npm packages installed during Create React App setup.

3. Limited customization options

Create React App is a black box.

To add new libraries and customize it you must always depend on Craco and the rest.

Before CRA v5 Craco was pretty much our only hope to get Tailwind support.

I remember all those times when I was creating Electron + React desktop apps, and I had to install react-app-rewired just to set the target to 'electron-renderer' to access Node modules in the React part of the app.

For total control you can always do npm eject, but of course then you become responsible for all the dependencies installed by CRA, which has trade-offs that may not be worth it for you.

What to use instead of Create React App

Old habits die hard, sure.

If you’ve been hesitating to switch to a superior tool, hopefully, the React team dumping CRA should finally do it for you.

There are far more modern and better-maintained alternatives out there; let’s check out some of the most popular ones:


Try Vite.

It’s a fast way to develop modern web projects.

It’s designed for efficiency. It makes development smoother.

The VueJS team is responsible for developing and maintaining Vite, so you can trust that it’s a reliable tool.

Even if you’re working with React, Vite has full support, including features like instant page refreshing and Hot Module Replacement (HMR).

Plus, it compiles with Babel, bundles with ESBuild, and provides just about everything CRA does – but better!

Vite logo

Vite is lightning-fast. Installing dependencies is quick. The dev team is always improving its speed.

You don’t worry about outdated dependencies or vulnerable packages.

Zero, zilch, nada, nothing. Up Vite!

Another great thing about Vite: it’s always being updated.

It has excellent support.

It’s compatible with many plugins. Vite quickly provided support for Tailwind 3.0 after it first came out.

You don’t need to worry about workarounds or extra steps. Just install Tailwind and its dependencies, do a little config, and you’re good to go!


It’s perfect for quick web projects.

It’s a zero-config bundler with no setup required.

Great for beginners and quick starts.

It’s also still more flexible than Create React App (CRA), which needs more customization work.

Parcel supports all the latest web technologies, including React, Vue, and TypeScript, and it can bundle any type of asset, from images and CSS to HTML and JavaScript.

Plus, it has excellent performance, thanks to its use of worker processes, which allows it to parallelize tasks and speed up builds.

Parcel logo

Parcel also has HMR support.

Hot Module Replacement: code changes show instantly in the browser. No need for a full page refresh.

This saves you time and makes development more efficient.

Finally, Parcel is updated often with the latest features and optimizations. You can trust the team to keep up with the latest web technologies and best practices.

The result: your projects are built with the best tools available.

Custom Webpack configuration

Webpack is still king.

The level of control of flexibility it gives you is unmatched.

There’s a massive number of options you can set, far more than just path and filename.

Give it a try to break free from the limitations of Create React App.

Webpack logo
Still one of the best

Another benefit to note: Webpack can generate a development build without a local server. Unlike Parcel and others.

This particular feature saved me when I was debugging React in a VSCode extension.

Loading data from localhost didn’t work properly for a Webview; I had to build to a local file and read the contents.

With source maps enabled and dev mode turned on, I could easily see the exact line where the error occurred in the TS code. Unlike with Parcel, where all I got was a minified build.

Of course setting up a Webpack config can be complex and more involved. You must configure various options to get everything working right.

But once you’re set up, you’ll have a heavily personalized build process for your project.

It’s a trade-off that may or may not be worth it for you.

Frameworks to replace Create React App

Need React’s state and UI capabilities without extras? The options above are great.

Want high-performance apps with advanced features like SSR? Consider frameworks with built-in routing, data fetching, etc.

Frameworks like:


Many of us know Next.js already.

Next.js has advanced features like server-side rendering (SSR) and static website generation.

Server-side rendering renders pages on the server and sends them to the client.

SSR greatly boosts performance, UX, and SEO.

Next.js logo

Use npx create-next-app instead of Create React App.

It sets up a new project with all dependencies and configurations for Next.js. This makes getting started easy.

So, if you’re looking to enhance your React app’s performance and capabilities, it’ll be great if you start exploring what Next.js has to offer.

With its advanced features and easy setup, it could be just what your app needs to deliver an exceptional user experience.


Explore Remix for superior performance.

Get advanced features and easy setup.

Deliver exceptional user experiences with this flexible and extensible framework.

npx create-remix my-app sets every up beautifully.

Remix React logo

With Remix, you’ll have access to a wide range of features and capabilities that speed up your development process.

And because it’s a newer framework, you’ll be able to take advantage of the latest technologies and best practices.


Gatsby is another awesome one – for scalable static sites.

Gatsby creates fast-loading and consistent sites with React.

Use npm init gatsby to set up a new project with all the necessary dependencies and configurations.

Once your app is set up, you can start building your website using the many plugins and tools available in the Gatsby ecosystem.

Gatsby logo

Gatsby has many resources for developers.

Starter kits, plugins, etc., that add functionality to your site.

You can easily build a blog, e-commerce site, or portfolio with Gatsby’s tools.

With Gatsby, you’ll benefit from its ease of use, powerful features, and vast ecosystem.

It’s a framework that’s definitely worth exploring if you’re looking for a flexible, high-performance way to build React apps. So why not give it a try and see what you can create?

Final thoughts

CRA was a popular tool.

But it had its limitations and issues.

It was bulky, outdated, and had few ways to customize it.

There are now more modern and lightweight alternatives to use.

Vite, Parcel, Custom Webpack, Next.js, Remix, and Gatsby are all great options for developers looking to enhance their React app’s performance and capabilities.

They offer unique features, flexibility, and customization options, making them ideal for a variety of projects.

It’s time to embrace the change and explore these new tools to take advantage of the latest technologies and best practices.

Whether you’re a beginner or an experienced developer, there’s an option out there that’s perfect for your needs.

So, don’t hesitate to try them out and see what you can create!

Stop using nested ifs. Use this instead

Typical use case for nested ifs: you want to perform all sorts of checks on some data to make sure it’s valid before finally doing something useful with it.

Don’t do this! :

function sendMoney(account, amount) { if (account.balance > amount) { if (amount > 0) { if (account.sender === 'user-token') { account.balance -= amount; console.log('Transfer completed'); } else { console.log('Forbidden user'); } } else { console.log('Invalid transfer amount'); } } else { console.log('Insufficient funds'); } }

There’s a better way:

function sendMoney(account, amount) { if (account.balance < amount) { console.log('Insufficient funds'); return; } if (amount <= 0) { console.log('Invalid transfer amount'); return; } if (account.sender !== 'user-token') { console.log('Forbidden user'); return; } account.balance -= amount; console.log('Transfer completed'); }

See how much cleaner it is? Instead of nesting ifs, we have multiple if statements that do a check and return immediately if the condition wasn’t met. In this pattern, we can call each of the if statements a guard clause.

If you do a lot of Node.js, you’ve probably seen this flow in Express middleware:

function authMiddleware(req, res, next) { const authToken = req.headers.authorization; if (!authToken) { return res.status(401).json({ error: 'Unauthorized' }); } if (authToken !== 'secret-token') { return res.status(401).json({ error: 'Invalid token' }); } if (req.query.admin === 'true') { req.isAdmin = true; } next(); }

It’s much better than this, right? :

function authMiddleware(req, res, next) => { const authToken = req.headers.authorization; if (authToken) { if (authToken === 'secret-token') { if (req.query.admin === 'true') { req.isAdmin = true; } return next(); } else { return res.status(401).json({ error: 'Invalid token' }); } } else { return res.status(401).json({ error: 'Unauthorized' }); } };

You never go beyond one level of nesting. We can avoid the mess that we see in callback hell.

How to convert nested ifs to guard clauses

The logic for this for doing this is simple:

1. Find the innermost/success if

Here we can clearly see it’s the cond3 if. After this, if we don’t do any more checks and take the action we’ve always wanted to take.

function func(cond1, cond2, cond3) { if (cond1) { if (cond2) { if (cond3) { console.log('PASSED!'); console.log('taking success action...'); } else { console.log('failed condition 3'); } } else { console.log('failed condition 2'); } } else { console.log('failed condition 1'); } }

2. Invert the outermost if and return

Negate the if condition to put the else statements’ body in there and add a return after.

Delete the else braces (keep the body, it still contains the formerly nested ifs, and move the closing if brace to just after the return.


function func(cond1, cond2, cond3) { if (!cond1) { // 👈 inverted if condition // 👇 body of former else clause console.log('failed condition 1'); return; // 👈 exit on fail } // 👇 remaining nested ifs to convert to guard clauses if (cond2) { if (cond3) { console.log('PASSED!'); console.log('taking success action...'); } else { console.log('failed condition 3'); } } else { console.log('failed condition 2'); } }

3. Do the same for each nested if until you reach the success if

And then:

function func(cond1, cond2, cond3) { if (!cond1) { console.log('failed condition 1'); return; } if (!cond2) { console.log('failed condition 2'); return; } // 👇 remaining nested ifs to convert if (cond3) { console.log('PASSED!'); console.log('taking success action...'); } else { console.log('failed condition 3'); } }

And finally:

function func(cond1, cond2, cond3) { if (!cond1) { console.log('failed condition 1'); return; } if (!cond2) { console.log('failed condition 2'); return; } if (!cond3) { console.log('failed condition 3'); return; } console.log('PASSED!'); console.log('taking success action...'); }


Inverting if statements in VS Code is easy when you install the JavaScript Booster extension.

Inverting an if/else statement with the JavaScript Booster VSCode extension.

Here we only had to put the cursor in the if keyword and activate the Show Code Actions command (Ctrl + . by default)

For a comprehensive of helpful VSCode extensions, you should definitely install along with JavaScript Booster, check out this awesome article.

Tip: split guard clauses into multiple functions and always avoid if/else

What if we want to do something other after checking the data in an if/else? For instance:

function func(cond1, cond2) { if (cond1) { if (cond2) { console.log('PASSED!'); console.log('taking success action...'); } else { console.log('failed condition 2'); } console.log('after cond2 check'); } else { console.log('failed condition 1'); } console.log('after cond1 check'); }

In this function regardless of cond1‘s value, the 'after cond1 check' the line will still print. Similar thing for the cond2 value if cond1 is true.

In this case, it takes a bit more work to use guard clauses:

If we try to use guard clauses, we’ll end up repeating the lines that come after the if/else checks:

function func(cond1, cond2) { if (!cond1) { console.log('failed condition 1'); console.log('after cond1 check'); return; } if (!cond2) { console.log('failed condition 2'); console.log('after cond2 check'); console.log('after cond1 check'); return; } console.log('PASSED!'); console.log('taking success action...'); console.log('after cond2 check'); console.log('after cond1 check'); } func(true);

Because the lines must be printed, we print them in the guard clause before returning. And then, we print it in all(!) the following guard clauses. And once again, in the main function body if all the guard clauses were passed.

So what can we do about this? How can we use guard clauses and still stick to the DRY principle?

Well, we split the logic into multiple functions:

function func(cond1, cond2) { checkCond1(cond1, cond2); console.log('after cond1 check'); } function checkCond1(cond1, cond2) { if (!cond1) { console.log('failed condition 1'); return; } checkCond2(cond2); console.log('after cond2 check'); } function checkCond2(cond2) { if (!cond2) { console.log('failed condition 2'); return; } console.log('PASSED!'); console.log('taking success action...'); }

Let’s apply this to the Express middleware we saw earlier:

function authMiddleware(req, res, next) { checkAuthValidTokenAdmin(req, res, next); } function checkAuthValidTokenAdmin(req, res, next) { const authToken = req.headers.authorization; if (!authToken) { return res.status(401).json({ error: 'Unauthorized' }); } checkValidTokenAdmin(req, res, next); } function checkValidTokenAdmin(req, res, next) { const authToken = req.headers.authorization; if (authToken !== 'secret-token') { return res.status(401).json({ error: 'Invalid token' }); } checkAdmin(req, res, next); } function checkAdmin(req, res, next) { if (req.query.admin === 'true') { req.isAdmin = true; } next(); }

In a way, we’ve replaced the if/else statements with a chain of responsibility pattern. Of course, this might be an overkill for simple logic like a basic Express request middleware, but the advantage here is that it delegates each additional check to a separate function, separating responsibilities and preventing excess nesting.

Key takeaways

Using nested ifs in code can lead to complex and hard-to-maintain code. Instead, we can use guard clauses to make our code more readable and linear. We can apply guard clauses to different scenarios and split them into multiple functions to avoid repetition and split responsibilities. By adopting this pattern, we end up writing cleaner and more maintainable code.