Tagged "web development"
Why are Side Effects Bad?
Side effects are any observable change to the state of an object. Some people might qualify this by saying that side effects are implicit or unintended changes to state.
Mutator methods, or setters, which are designed to change the state of an object, should have side effects. Accessor methods, or getters, should not have side effects. Other methods should generally try to avoid changing state beyond what is necessary for the intended task.
Why are these guidelines best practices though? What makes side effects so bad? Students have asked me this question many times, and I've worked with many experienced programmers who don't seem to understand why minimizing side effects is a good goal.
For example, I recently commented out a block of my peer's code because it had detrimental side effects. The code was intended to add color highlighting to log entries to make his debugging easier. The problem with the code was that the syntax highlighting xml was leaking into our save files. He was applying the highlight to a key, then using that key both in the log and in the save file. Worse still, he wrote this code so that it only occurred on some platforms. When I was debugging a cross platform feature, I got very unpredictable behavior and ultimately traced it back to this block.
This is an example where code was intended for one purpose, but it also did something else. That something else is the side effect, and you can see how it caused problems for other developers. Since his change was undocumented and uncommented, I spent hours tracking it down. As a team, we lost significant productivity due to a side effect.
Side effects are bad because they make a code base less agile. Side effects cause bugs that are difficult to find, and lead to code that is more difficult to maintain.
Before I continue, let me be clear that all code style guidelines should be broken sometimes. For each situation, a guideline or design pattern may be better or worse, and I recognize that we are always working in shades of gray.
Generally, a block of code should be written for one purpose. If it is a method, then it should do one thing. If another thing needs to be done with an object, then that should be encapsulated in another method.
Here's a hypothetical example that I've seen played out hundreds of times in my career.
A class needs to do task X. A programmer may write a method to do task X, but he accidentally includes logic that also does task Y. Later, he may see that he needs to do task Y all alone. So he writes a method to do task Y. That's where the problem is compounded.
Later still, the definition of task Y changes. So another programmer has to rewrite task Y. He goes to the class, changes the method for task Y alone, does a few quick tests, and proceeds on his merry way.
Then mysterious bugs start occurring. QA can't really track them down to one thing because they only occur sporadically after task Y. Finally, it takes many man-hours to remove task Y from the method for task X.
In this example, the side effect led to code duplication, which led to trouble when updating the code, which led to bugs that cost many hours to track down. The fewer side effects you introduce, the easier your code will be to maintain.
These two examples show how side effects can derail development and why they are so inimical. We all write code with side effect occasionally, but it's our job to figure out how to do it in a way that doesn't make the code more difficult to maintain.
How Can Comments be Controversial?
Comments add information to your code. They don't impact the execution of that code. So how can they be bad? I believe that more commenting is better, and that comments are a vital tool in maintaining an agile codebase, but I've met many experienced developers who argue with me about comments.
The Pragmatic Programmer is an excellent book. It outlines best practices that have solidified over the last thirty years or so as software development has grown as a field. The authors share these practices as a series of tips that are justified by real world anecdotes. In a section on comments the authors say
"The DRY principle tells us to keep the low-level knowledge in the code, where it belongs, and reserve the comments for other, high-level explanations. Otherwise, we're duplicating knowledge, and every change means changing both the code and the comments. The comments will inevitably become out of date, and untrustworthy comments are worse than no comments at all"
They make the point that obsolete comments are worse than no comments at all. This is undoubtably true, but it has been bastardized by many programmers as an excuse for not commenting. I've heard this excuse, along with two others to justify a lack of comments in code.
- The code is self-commenting.
- Out of date comments are worse than no comments.
- You shouldn't rely on comments.
In this post I want to address each of these points. I think that thorough commenting speeds up the rate of consumption of code, and that obsolete comments are a failure in maintenance, rather than in comments in general.
1. The code is self-commenting
This is the excuse I hear most often from developers who don't comment their code. The thinking is that clear variable and method names replace the need for comments. There are situations where this is true. For very short methods with a single purpose, I see that no additional comments may be necessary.
But most methods are not very short, and most methods achieve several things that relate to one overall purpose. So in the first place, very few methods fit the bill.
This is not a valid complaint because good comments only make the code easier to read. While the code may be legible on its own, a comment that explains the purpose of a method, or the reason for a string operation, can only make it easier to read. The author of Clean Code agrees due to the amount of time we spend reading code.
"the ratio of time spent reading vs. writing is well over 10:1. We are constantly reading old code as part of the effort to write new code. Because this ratio is so high, we want the reading of code to be easy, even if it makes the writing harder."
Because we spend more time reading code than writing it, a responsible programmer knows that the extra comment in a method which takes very little time to write may well save another programmer many hours of learning.
2. Out of date comments are worse than no comments
To refute this argument, lets take cars as an analogy. If you buy a car, then drive it for twenty thousand miles without an oil change, it will break down. If you did that, the fault wouldn't be in cars in general. Rather, the fault is in the lack of maintenance.
Similary, obsolete comments are not a failure of comments in general, but a failure of the programmer who updated the code without updating the comments. That other programmers have bad practices does not justify continuing that bad practice.
Obsolete comments are a broken window. If you see them, it is your job to fix them.
So while obsolete comments ARE worse than no comments, that argument is an argument against bad coding practices, not comments in general.
3. You shouldn't rely on comments
The argument that a programmer shouldn't rely on comments is a subtle dig at another programmer's skill. They're saying "a good programmer can understand this code with no comments."
There is a grain of a valid point there. If you consider yourself a seasoned programmer, then you should be accustomed to wading through large volumes of old code and parsing out the real meaning and functionality.
But even for veteran programmers, comments make it easier to read and understand the code. It's much easier to interpret a one line summary of a block of code than to parse that block line by line. It's much easier to store that one line in your head than it is to store the whole block in your head.
The real reason why this is the worst of excuses is that not all programmers working on your code are senior programmers. Some are green graduates straight out of college. Yes, a more experienced programmer could understand the code without comments, but it will cost that graduate hours to figure out what they could have gotten in minutes with good comments.
My Commenting Principle
Comment your code so that a green programmer can understand it.
Or…
Comment your code so that your boss can understand it.
What does it take to make your code readable by a green graduate? Imagine them going through it line by line. If there are no comments, then they need to understand every function call, and every library routine. They need to understand a bit about your architecture and date model.
Now imagine that every single line is commented.
In that scenario, a green programmer can read your file by reading one comment after the next. If they need more information, they can still drill into the code and get the details, but they can understand the overview just through comments.
The closer you can get to that, the better.
Don't listen to the arguments against thorough commenting habits. Just point the detractors here.
Why I Created QuizMana.com
When I was teaching at UCSC and SJSU, I taught very large courses, often consisting of over 200 students. Courses that size create lots of infrastructural problems. Communication is a huge problem, but that's mostly taken care of by Canvas or Blackboard. The bigger problem for me was assessments.
I think that even well-written multiple choice quizzes are not great assessments. In multiple choice quizzes, students always have the answer presented to them. Even very difficult multiple choice quizzes never ask students to explain their reasoning. They never force students to come up with a concept from memory. They present students with the false premise that knowledge is fixed, and is simply a matter of choosing from what already exists.
So I always wanted to use other types of questions, and I did. Or at least I tried to. To incorporate even one short answer question meant hours of grading. I once did a single essay assignment for a large class, and probably spent over forty hours grading essays.
I needed a grading tool that could quickly save me time, while allowing me to diversify the types of assessments I could use on a week-to-week basis. For the past three months I've been building a website that automatically grades short answer, essay, and multiple choice questions. I call it QuizMana.com, and it's accepting applicants for the closed beta right now.
The hook of the website is automatic grading, but the purpose of it is to save teachers as much time as possible. So the grader gives a score for every question, but the site then points the teacher to all the "low confidence" scores. These are the scores where the grader didn't have enough information to be fully confident it gave an accurate score. The teacher can then grade the low confidence responses on a single page, and then they are done!
So the grader is part of a larger process that means that teachers only grade the work that actually needs their attention.
I think this can be a great tool, and I think it will save teachers a lot of time. If you're teaching a large class this semester, then give yourself a new tool, and try QuizMana.
How to host a static site on AWS
In this post, I'm going to show you how you can put up a simple html webpage on AWS using resources that are usually free. It's probably the cheapest way to set up a website.
Prerequisites
There are a few things you need to do ahead of time.
- Sign up for AWS
- Create a webpage
For the first one, head on over to https://aws.amazon.com/console/ and create a new account.
For the second one, you will probably need to learn a little coding to make it happen, but I'll give you something simple to start with.
<!DOCTYPE html>
<html>
<head>
<title>My Webpage</title>
</head>
<body>
<main>
<h1>My Webpage</h1>
<p>
This is a simple html webpage hosted on AWS S3 and AWS Cloudfront.
</p>
<p>
This was built based on <a href="https://evanxmerz.com/post/how-to-host-a-static-site-on-aws" target="_blank">a tutorial at evanxmerz.com/post/how-to-host-a-static-site-on-aws</a>.
</p>
</main>
</body>
</html>
There are some optional prerequisites that I'm intentionally omitting here. If you want to host your site on your own domain, then you'll need to purchase that domain from a domain registrar, such as AWS Route 53. Then you would also need to get an SSL certificate from a certificate authority such as AWS Certificate Manager.
Create a public S3 bucket
For your site to exist on the internet, it must be served by a computer connected to the internet. To make this possible, we need to upload your html file to "the cloud" somehow. Don't worry, "the cloud" is simply a marketing term for a web server. In this tutorial, our cloud storage service is AWS S3.
First you need to create a bucket.
- Browse to the AWS S3 service
- Click "Create Bucket". A bucket is a container for files. You might use a different bucket for each of your websites.
- Enter a name for your bucket. I name my static site buckets after the site they represent. So empressblog.org is in a bucket called empressblog-org. I named my bucket for this tutorial "static-site-tutorial-1-evanxmerz-com" because I am going to connect it to static-site-tutrial-1.evanxmerz.com.
- Next, select the AWS region for your bucket. The region is not really important, but you must write down what you select, because you will need it later. I selected "us-west-1".
- Under "Object Ownership" select "ACLs enabled". This will make it easier for us to make this bucket public.
- Under "Block Public Access settings for this bucket", unselect "Block all public access", then click the checkbox to acknowledge that you are making the contents of this bucket public.
- Then scroll to the bottom and click "Create Bucket".
Next, you need to allow hosting a public website from your bucket.
- Locate your bucket in the bucket list in S3, then click it.
- You should now be looking at the details for your bucket. Click the "Properties" tab, then scroll down ti "Block all public access" and click "Edit".
- Click "Enable", then under "Index document", enter "index.html". Then click "Save changes".
- Click the "Permissions" tab for your bucket. Scroll down to "Access control list (ACL)" and click "Edit". Next to "Everyone (public access)", click the box that says "Read". Then click the box that says "I understand the effects of these changes on my objects and buckets" and click "Save changes".
Create your index page
This step assumes that you have already created an html page that you want to make public. You can also upload other pages, css files, and javascript, using this same procedure.
- Find the "Objects" tab for your bucket on S3.
- Click "Upload".
- Click "Add files".
- Browse to your index.html
- Scroll down to the "Permissions" accordion and expand it.
- Click "Grant public-read access" and the checkbox to say that you understand the risk.
- Then click "Upload".
Now your page is on the internet. You can go to the objects tab for your bucket, then click on your file. That should display a link called "Object URL". If you click that link, then you should see your page. My link is https://static-site-tutorial-1-evanxmerz-com.s3.us-west-1.amazonaws.com/index.html.
Why to make a CloudFront distribution
Now you have seen your file on the web. Isn't that enough? Isn't this tutorial over? No. There are several problems with using S3 alone.
- Your files will be served from S3, which is not optimized for worldwide distribution. Your files have a home on a server in the region you selected. If that region is in the US, then people in Asia are going to have a much slower download experience.
- Your site will not work if there are errors. Your index page works fine, but what happens if you entered an incorrect link, and someone ends up at indec.html? You will get a nasty error message from AWS, rather than being redirected to a page on your site.
The final problem is the URL. This can be solved by setting up a domain in Route 53, but it's much wiser to set up a CloudFront distribution, then connect your domain to that.
Set up a CloudFront distribution
AWS CloudFront is a Ccontent Distribution Network (CDN). A CDN is a network of servers all over the world that are close to where people live. So people in Asia will be served by a copy of your page on a server in Asia.
- Find the CloudFront service on AWS.
- Click "Create distribution".
- In the "Origin domain" field you must enter your S3 bucket's Static Website Hosting Endpoint as the CloudFront origin. You can find this on the "Properties" tab of your S3 bucket. So for me, that was "static-site-tutorial-1-evanxmerz-com.s3-website-us-west-1.amazonaws.com".
- Then scroll all the way down to "Alternate domain name (CNAME)". This is where you would enter a custom domain that you purchased from AWS Route 53 or another registrar. For instance, if you want to set up your site on mystore.com, then you would enter "*.mystore.com" and "mystore.com" as custom domains. I entered "static-site-tutorial-1.evanxmerz.com" as my custom domain because that's where I'm putting up this tutorial.
- Then go to the "Custom SSL certificate" field. If you do not have your own domain, then you can ignore this. But if you have your own domain, then you will need your own SSL certificate. The SSL certificate is what enables private web browsing using the "https" protocol. Go set up a certificate using AWS Certificate Manager before setting up CloudFront if you want to use a custom domain.
- Finally, click "Create distribution"
Then you need to modify the distribution to act more like a normal web server. So we will redirect users to index.html if they request an invalid url.
- Find your distribution on CloudFron and click it.
- Click the "Error pages" tab.
- Click "Create custom error response".
- Under "HTTP error code" select "400: Bad Request".
- Under "Customize error response" click "Yes".
- Under "Response page path" enter "/index.html".
- Under "HTTP Response code" select "200: OK".
- Click "Create custom error response".
- Repeat these steps for 403 and 404 errors.
Then, if you have a custom domain, you need to go to AWS Route 53 and enable it.
- Go to Route 53 and select the hosted zone for your domain.
- Click "Create record".
- Create an A record for your domain or subdomain.
- Under "Route traffic to" click "Alias".
- Click "Alias to CloudFront distribution" and select your distribution.
- Click "Create records".
Now if you visit your custom domain, you should see your page. Here's mine: https://static-site-tutorial-1.evanxmerz.com/
Congratulations!
Congratulations! You've erected your first website using AWS S3 and AWS CLoudFront! Let's review the basic architecture here.
- Files are stored in a cloud file system called AWS S3.
- Files are served by a Content Distribution Network (CDN) called AWS CloudFront.
- Optionally, domains are routed to your CloudFront distribution by AWS Route 53.
- Optionally, CloudFront uses an SSL certificate from AWS Certificate Manager.
This is about the simplest and cheapest architecture for hosting a fast static site on the internet.
It's important to note that this is also the simplest way for hosting ANY static site. If you generated a static site using React, Gatsby, or Next, then you could host them in the same way.
IT's also important to note that this architecture fails as soon as you need to make decisions server side. This architecture works fine for websites that are frontend only websites, where you don't interact with private data. Once you need private data storage, an API, or custom logic on a page, then you will need a server in some variety. There are multiple solutions in that case, from the so-called "serverless" AWS Lambda, or the more old-fashioned AWS EC2, which is simply a server farm.
But you are now ready to start exploring those more complex options.
How to conquer Google Core Web Vitals
In 2021, Google finally launched their long awaited Core Web Vitals. Core Web Vitals are the measurements that Google uses to measure the user experience (UX) on your website. These include metrics tracking things like how long the page takes to load images, how long it takes to become interactive, and how much it jitters around while loading.
Core Web Vitals are important to all online businesses because they influence where your site appears on Google when users are searching for content related to your site. They are far from the only consideration when it comes to search engine optimization (SEO), but they are increasingly important.
In this article, I'm going to give you a high level overview of how you can get green scores from Google's Core Web Vitals. We're not going to dig into implementation level details, yet, but we will look at the general strategies necessary to optimize the scores from Google.
This article is informed by my experience optimizing Core Web Vitals scores for the websites I manage and develop professionally. It's also informed by my study and experimentation on my own websites and projects.
Prepare for a long battle
Before we begin, I want to set your expectations properly. On any reasonably large website, Core Web Vitals scores cannot be fixed in a night, or a weekend, and maybe not even in a month. It will likely take effort each week, and in each development project, to ensure that your scores get to the highest level and stay there.
This is the same expectation we should have for any SEO efforts. It takes time to get things right in the code, the architecture, and in the content. All three of those things must work together to put your site on the first page of Google search results for high volume search terms.
In my experience, a successful SEO operation requires coordination of web developers, SEO experts, writers, and whoever manages the infrastructure of your website.
1. Understand Core Web Vitals
The first step to conquering Core Web Vitals is to understand what they are measuring. So get access to Google Search Console, and start looking at some reports. Use PageSpeed Insights to start looking at the values that Google is actually calculating for your site.
Don't be scared if your scores are really low at first glance. Most sites on the internet get very low scores because Google's standards are incredibly high.
You should see some aronyms that represent how your site did on key metrics. There are four metrics that are used to calculate the Core Web Vitals score for a page.
- LCP = Largest Contentful Paint.
- The LCP of a page is how long it takes for the largest thing to be shown on screen. Typically this is the banner image on a page, but PageSpeed Insights can sometimes get confused if you use header tags in the wrong places.
- FCP = First Contentful Paint.
- The FCP of a page is how long it takes for anything to appear on screen.
- CLS = Cumulative Layout Shift.
- The CLS is a measure of how much elements on the page jitter around while the page is loading.
- FID = First Input Delay.
- FID is how long it takes for the page to respond to a click.
2. Make the right architecture decisions when starting a new site
Most people can't go back in time and choose a different platform for their site. Most people are stuck with the architectural decisions made at the beginning of the project. If you're in that category, then don't fret. The next section will talk about how you can maximize existing infrastructural technologies to get the maximum performance out of your website.
On the other hand, if you are starting from scratch then you need to critically consider your initial architectural decisions. It's important to understand how those architectural decisions will impact your Core Web Vitals score, and your search ranking on Google.
Client side rendering vs. Sever side rendering
The most important decision you have to make is whether you will serve a site that is fully rendered client side or a site that renders pages on the server side.
I want to encourage you to choose a site architecture that is fully client side rendering, because client side rendering provides huge benefits when it comes to both scaling and SEO, and the drawbacks previously associated with client side rendering are no longer relevant.
Imagine the typical render process for a webpage. This process can be broken down into many steps, but it's important to realize that there are 3 phases.
- The server fetches data from a data store.
- The server renders the page.
- The client renders the page.
Steps 1 and 3 are unavoidable. If you have a backend of any complexity, then you must have a data store. If you want users to see your website, then their web browsers must render it.
But what about step 2? Is that step necessary? No. Even worse is the fact that it's often the most time-consuming step.
But if you build your site as a client-side js/html/css bundle that uses an API on the backend, then you can cut out the server side rendering entirely. If you want to maximize your site's Core Web Vitals scores, and hence your SEO, then you should cut out the server side rendering step if possible. Adding that step is inherently slower than strategies that eliminate it.
This means that platforms that are used for server side rendering are probably something to avoid in 2022 and beyond. So Ruby on Rails is probably a bad choice, unless you're only using it to build an API. Similarly, Next.js is probably a bad choice, unless you are only using it to generate a static site.
You should also look at benchmarks for various website architectures. You will see that vanilla React/Vue/Javascript sites generally outperform server side rendered sites by a wide margin.
But isn't a client side rendered site bad for SEO?
If someone hasn't done much SEO lately, then they may come up with this counterargument, that using client side rendering is bad for SEO. Up until around 2017, Google didn't run the javascript on web pages it was scanning. If the javascript wasn't executed, then a purely client side rendered page would show up as an empty page and never rank on Google. We had to come up with lots of workarounds to avoid that fate.
Since 2017, however, Google does run the javascript on the page. So having a purely client-side rendered site is perfectly fine.
Server side rendered sites are also harder to scale
I've scaled several websites in my career, so I can tell you that it's much more difficult to scale a server side rendered website than one that isn't server side rendered. To scale on the server side requires a complex balance of caching, parallelization, and optimization that is entirely unnecessary if your site is rendered client side.
It's true that you would still have to scale the API on the server side, but that's true in both cases.
3. Maximize mature web technologies
But what if you can't change the fundamental architecture of your website? Are you stuck? No. There are a number of web technologies that exist to mitigate the performance impact of various architectural decisions.
- Use a Content Distribution Network, especially for images. Take a look at services like ImgIX, which can optimize your images and play the role of a CDN. Other commonly used CDNs include CloudFront and CloudFlare.
- Optimize your cache layer. Try to hit the database as rarely as possible. The API that backs your site will get bottlenecked by something. Quite frequently, that something is your database. Use a cache layer such as REDIS to eliminate database queries where possible.
- Use geolocated servers. The CDN should take care of serving content from locations near your users, however, if requests are ever getting back to your web servers, make sure you have servers near your users.
- Enable automatic horizontal scaling of your server. You need to be able to add more servers during high traffic times, such as the holidays. To do so requires parallelizing your server code, and handling session across multiple servers. There are many ways to mitigate those problems, such as the sticky sessions feature offered by Heroku. You need to be able to scale horizontally at least on your API, if not on the servers that serve your site.
4. Monitor the results
Finally, you need to monitor Google Search Console every day. Google Search Console is one of the first things I check every time I open my work computer to begin my day. Make this a habit, because you need to be aware of the trends Google is seeing in the user experience on your site. If there is a sudden issue, or things are trending the wrong way, you need to proactively address the problem.
You should also use other site monitoring tools. Google's scores are a running 28 day average. You need a response time faster than 28 days. So I encourage you to set up a monitoring service such as Pingdom or Status Cake, which will give you real time monitoring of website performance.
Finally, you should run your own spot checks. Even if everything looks okay at a glance, you should regularly check the scores on your most important pages. Run PageSpeed Insights or Web Page Test on your homepage and landing pages regularly. Make sure to schedule time to take care of the issues that arise.
Conclusion
To conquer Google Core Web Vitals requires coordination of the effort of many people over weeks or months of time, but with a rigorous approach you can get to the first or second page of Google search results for high volume search terms. Make sure to spend time understanding Google Core Web Vitals. Ensure that the critical architectural decisions at the beginning of a project are in line with your long term goals. Maximize your use of mature web technologies to mitigate architectural issues. And monitor your site's performance every day.
With these strategies you can take your site to the next level.
What are common mobile usability issues and how can you solve them?
In this article, I'm going to show you some common mobile usability issues reported by Google, and show you how to fix them using basic css.
Mobile usability issues in Google Search Console
A few days ago I received this rather alarming email from Google.
I was surprised to see this, because, although my blog isn't the most beautiful blog on the web, I pride myself on the clarity for reading on any device. So I logged into Google Search Console and found the "Mobile Usability" link on the left side to get more information.
Search console gave me the same three issues.
- Viewport not set
- Text too small to read
- Clickable elements too close together
Search console said they were all coming from https://evanxmerz.com/soundsynthjava/Sound_Synth_Java.html. This url points at the file for my ebook Sound Synthesis in Java. This ebook was written using markup compatible with early generations of Kindle devices, so it's no surprise that Google doesn't like it.
When I opened the book on a simulated mobile device, I could see that the reported mobile usability issues were apparent.
Let's go through each of the issues and fix them one by one. This will require some elementary CSS and HTML, and I hope it shows how even basic coding skills can be valuable.
Viewport not set
Viewport is a meta tag that tells mobile devices how to interpret the page on a device. We need to provide it to give web browsers a basis for showing a beautiful page to viewers. In practical terms, this means that without a viewport meta tag, the fonts on the page will look very small to viewers on mobile devices because their browsers don't know how to scale the page.
To solve this issue, add this line to the html head section.
<meta name="viewport" content="width=device-width, initial-scale=1">
This may also solve the remaining issues, but we'll add some css for them just to be sure.
Add a CSS file
The following two fixes require us to use some css, so we will need to add a CSS file to our webpage.
Early kindles didn't support much css, so it was better to just format your books with vanilla html and let the Kindle apply formatting. That limitation doesn't apply to the web, though, so I created a file called styles.css in the same directory as my html file, and added this line within the head section of my html page. This tells the page to pull style information from the file called "styles.css".
<link rel="stylesheet" href="styles.css">
Next we need to add some style rules to fix the remaining mobile usability issues.
Text too small to read
Making the font larger is easy. We could do it for only mobile devices, but I think increasing the font size slightly will help all readers. The default font size is "1rem" so let's bump it up to 1.1 by adding the following code to our css file.
body {
font-size: 1.1rem;
}
Clickable elements too close together
The only clickable elements in this document are links. So this means that the text is too small, and the lines are too close together. The previous two fixes might also address this issue, but just in case they don't, let's make the lines taller by adding another line to our body css.
body {
font-size: 1.1rem;
line-height: 1.5;
}
The default line-height is 1. By moving it to 1.5, we are making lines 50% taller. This should make it easier for users to click the correct link.
Putting it all together
In addition to solving the mobile usability issues from Google, I wanted to ensure a good reading experience on all devices, so here's my final set of rules for the body tag in my css file.
At the bottom I added rules for max-width, margin, and padding. The max-width rule is so that readers with wide monitors don't end up with lines of text going all the way across their screens. The margin rule is an old-fashioned way of horizontally centering an element. The padding rule tells the browser to leave a little space above, below, and beside the body element.
body {
font-size: 1.1rem;
line-height: 1.5;
max-width: 800px;
margin: 0 auto;
padding: 10px;
}
When I opened up the result in a simulated mobile device, I could see that the issues were fixed.
How to make Dropbox ignore the node_modules folder
In this article I'm going to show you how to get Dropbox to ignore the node_modules folder.
Why code in a Dropbox folder?
I recently bought a new computer and experienced the pain of having to sync around 300Gb of files from one Windows machine to another. This was primarily due to the in-progress music projects on my computer.
So I decided that I would just work within my Dropbox folder on my new computer. In the past, I've spoken to colleagues who did this. They were mostly artists, and didn't use git.
What I didn't consider at the time, was that this would mean there would be multiple syncing mechanisms for my code. I would be sending the files both to Dropbox and Git.
Even worse, Dropbox locks files while it is working. So if you are running something like "npm install" or "npm update", those processes can fail because they can't work on a file at the same time as Dropbox.
I began experiencing these problems almost immediately, so I either had to find a way to ignore the node_modules folder in Dropbox or stop working on code within the Dropbox folder.
Dropbox ignore syntax
Fortunately, Dropbox gave us a way to ignore files or folders a few years ago. So for Windows users, the following command will ignore your node_modules folder. Please note that this needs to be done for every project, and that the command needs to be completed with your actual directory structure.
Set-Content -Path 'C:\Users\yourname\Dropbox\yourproject\node_modules' -Stream com.dropbox.ignored -Value 1
How to optimize largest contentful paint (LCP) on client side
In this article I'm going to show you some strategies I've used to optimize websites on the client side for largest contentful paint (lcp) and first contentful paint (fcp).
What is largest contentful paint and why does it matter?
Largest contentful paint (LCP) is a measure of how long from initial request it takes for your site to render the largest thing on screen. Usually it measures the time it takes your largest image to appear on screen.
First contentful paint (FCP) is a measure of how long from initial request it takes for your site to render anything on screen. In most cases, optimizing LCP will also optimize FCP, so this is the last time I'll mention FCP in this article.
Google Core Web Vitals considers 2.5 seconds on mobile to be a good LCP when loading the site at 4g speeds. This is an extremely high bar to reach, and most sites won't come close without addressing it directly.
It's also important to note that reaching an LCP of 2.5 seconds once is not sufficient to pass. Your pages must achieve an average LCP of under 2.5 seconds over a period of 28 days in order to pass. This means that putting off work on LCP will only be more painful in the future. You need to act now to move your LCP in the right direction.
Why not use server side rendering?
When searching for ways to optimize LCP, I came across various sites that suggested server side rendering. They suggest that rendering the page server side and delivering it fully rendered client side would be the fastest. I know from experience that this is wrong for several reasons.
First, you still need to render on client side even if you deliver a flat html/js/css page. The client side still needs to extract and compile the page, and that takes the bulk of rendering time for modern, js-heavy webpages.
Second, rendering server side can only possibly be faster if your site isn't scaling. Yes, when you have a small number of simultaneous users, it's much faster to render on your server than on an old android. Once you hit hundreds or thousands of simultaneous users that math quickly flips, and it's much faster to render on thousands of client machines, no matter how old they are.
Why not use service workers?
Another suggestion I see is to use service workers to preload content. Please keep in mind that Google only measures the first load of a page. It does not measure subsequent loads. So any technique that improves subsequent loads is irrelevant to Google Core Web Vitals. Yes, this is incredibly frustrating, because frameworks like Next.js give you preloading out of the box.
Optimize images using modern web formats and a CDN
The most important thing you can do to achieve a lower LCP is to optimize the delivery of your images. Unless you use very few images, as I do on this blog, then images are the largest part of the payload for your webpages. They are typically around 10 to 100 times larger than all other assets combined. So optimizing your images should be your number one concern.
First, you need to be using modern web image formats. This means using lightweight formats such as webp, rather than heavier formats such as png.
Second, you need to deliver images from a content distribution network (CDN). Delivering images from edge locations near your users is an absolute must.
Third, you need to show images properly scaled for a user's device. This means requesting images at the specific resolution that will be displayed for a user, rather than loading a larger image and scaling it down with css.
Finally, Google seems to prefer progressive images, which give the user an image experience such as a blurred image before the full image has loaded into memory. There are many robust packages on the web for delivering progressive images.
I suggest you consider ImgIX for optimizing your images. ImgIX is both an image processor and a CDN. With open source components that work with various CMSs, and in various environments, ImgIX is a one stop shop that will quickly solve your image delivery issues. I've used it at two scaling websites, and in both cases it has been extremely impactful.
Deliver the smallest amount of data to each page
After you optimize your images, the next thing to consider is how much data you are sending to the client. You need to send the smallest amount of data that is necessary to render the page. This is typically an issue on list pages.
If you're using out-of-the-box CRUD APIs built in Ruby on Rails, or many other frameworks, then you typically have one template for rendering one type of thing. You might have a product template that renders all the information needed about a product. Then that same product template is used on product detail pages, and on list pages. The problem with that is that much less information is needed on the list pages. So it's imperative that you split your templates into light and heavy templates, then differentiate which are used in which places.
This is more of a backend change than a frontend change, but optimizing frontend performance requires the cooperation of an entire team.
Deliver static assets using a CDN
After putting our images through ImgIX, we stopped worrying about CDNs. We thought that because images were so much larger than the static assets, it wouldn't make much difference to serve static assets from our servers rather than a CDN.
This is true, if you are just beginning to optimize your frontend performance. Putting static assets on a CDN won't lead to a tremendous drop in LCP.
However, once you are trying to get your page load time down to the absolute minimum, every little bit counts. We saved an average of around two tenths of a second on our pages when we put our static assets on a CDN, and two tenths of a second is not nothing.
Another great thing about putting your static assets on a CDN is that it typically requires no code changes. It's simply a matter of integrating the CDN into your continuous integration.
Eliminate third party javascript
Unfortunately, third party javascript libraries are frequently sources of a significant amount of load time. Some third party javascript is not minimized, some pulls more javascript from slow third party servers, and some uses old fashioned techniques such as document.write.
To continue optimizing our load time we had to audit the third party javascript loaded on each page. We made a list of what was loaded where, then went around to each department and asked how they were using each package.
We initially found 19 different trackers on our site. When we spoke with each department we found that 6 of them weren't even being used any more, and 2 more were only lightly used.
So we trimmed down to 11 third party javascript libraries then set that as a hard limit. From then on, whenever anyone asked to add a third party library, they had to suggest one they were willing to remove. This was a necessary step to meet the aggressive performance demands required by Google.
Optimize your bundle size
The final thing to do to optimize your client side load time is to optimize your bundle size. When we talk about bundle size, we're talking about the amount of static assets delivered on your pages. This includes javascript, html, css, and more. Typically, extracting and compiling javascript is what takes the bulk of the time, so that's what you should focus on.
Use code splitting
Code splitting means that your app generates multiple bundles that are potentially different for each page. This is necessary in order to deliver the smallest amount required for a given page. Most modern website transpilers like WebPack will do this automatically.
Forget import *
Stop using "import *" entirely. You should only ever import the methods you are using. When you "import *" you import every bit of code in that module as well as every bit of code that it relies on. In most circumstances, you only need a fraction of that.
It's true, that a feature called tree shaking is able to eliminate some of the cruft in scenarios where you're importing more than you need, but it's sometimes tough to figure out where the tree shaking is working and where it's failing. To do so, you need to run bundle analysis, and comb through it carefully.
It's much easier to simply stop using "import *".
Use composition wisely
I almost named this section "Forget the factory pattern", because the factory pattern creates situations very similar to "import *". In the factory pattern, a method is called that returns an object with all the methods needed to fulfill a responsibility or interface. What I see most often, is a misapplication of the factory pattern whereby programmers are dumping a whole bunch of methods into a pseudo-module then using only one or two methods.
// don't do this
const createDateHelpers = () => {
const formatDate = () => {...};
const dateToUtc = () => {...};
return {
formatDate,
dateToUtc,
}
}
You can see that if you want to call "formatDate", then you need to run "createDateHelpers().formatDate()". This is essentially the same as importing every method in the date helpers module, and again, you are importing all their dependencies as well.
This is where composition can be applied to make an object that gives you the full object when needed, but also allows you to export methods individually.
// use composition
export const formatDate = () => {...};
export const dateToUtc = () => {...};
export default const createDateHelpers = () => {
return {
formatDate,
dateToUtc,
}
};
Render a simplified static page
It's important to note that optimizing your existing pages isn't the only strategy available. Amazon's website crushes Google's Core Web Vitals, even though it isn't very fast. It does this by rendering a simplified static template for the first load, then filling it in with the correct content. So if you visit Amazon, you may see some evergreen content flash up on the page before content specific to you loads in.
That's a fine way to pass Google's Core Web Vitals, but it isn't optimizing the performance of your page. That's tailoring your page to meet a specific test. It's not cheating, but it's not necessarily honoring the intention of Google's UX metrics.
Conclusion
There are two basic categories of changes that are necessary to optimize the client side of a website for largest contentful paint: optimizing delivery of images, and optimizing delivery of code. In this article I've listed several strategies I've used in the past to address both of these categories. These strategies include using progressive images, using CDNs, and delivering as little data and code as is necessary to render a page.
How to measure the performance of a webpage
Measuring the performance of a webpage is an extremely complex topic that could fill a book. In this article I'm going to introduce some of the commonly used tools for measuring web performance, and show you why you need to take multiple approaches to get a complete picture of the performance of any webpage.
Why measure webpage performance?
Why measure webpage performance? Performance is so critical to the modern web that this question seems almost comical. Here are just a few reasons why you should be measuring and monitoring webpage performance.
- User experience impacts Google search rankings via Core Web Vitals.
- Page load time correlates with conversion rate and inversely correlates with bounce rate.
- Pages must be performant to be accessible to the widest possible audience.
Four approaches to measuring performance
No single tool is going to give you a comprehensive perspective on webpage performance. You must combine multiple approaches to really understand how a page performs. You must look at multiple browsing patterns, multuple devices, multiple times of day, and multiple locations.
In this article, I'm going to talk about four different approaches to measuring the performance of a page and recommend some tools for each of them. You must use at least one from each category to get a complete picture of the performance of a webpage.
The four approaches I'm going to talk about are...
- One time assessments
- Live monitoring and alerts
- Full stack observability
- Subjective user tests
Why do we need multiple perspectives to measure performance?
People who are new to monitoring web performance often make mistakes when assessing web performance. They pull up a website on their phone, count off seconds in their head, then angrily email the developers complaining that the website takes 10 seconds to load.
The actual performance experienced by users of your website is an important perspective, but there are dozens of things that can impact the performance of a single page load. Maybe the page wasn't in the page cache. Maybe the site was in the middle of a deploy. Maybe hackers are attacking network infrastructure. Maybe local weather is impacting an ISP's network infrastructure.
You can't generalize from a single page load to the performance of that page. I've seen many people fall into this trap, from executives to marketing team members. It makes everyone look bad. The website looks bad because of the slow page load. The developers look bad because of the poor user experience. The person who called it out looks bad because it looks like they don't know what they are doing. The data analysts look bad because they aren't exposing visualizations of performance that other team members can use.
So read this article and fire up some of the tools before sending out that angry email.
Please note that the tools listed here are just the tools that I have found to be effective in my career. There are many other options that may be omitted simply because I've never used them.
One time assessments
One time assessments are the most commonly used tools. One time assessments can be run against a production webpage at any time to get a perspective of the performance at that moment. One thing that's nice about these tools is that they can be used effectively without paying anything.
PROS:
- Easy to use
- Easy to quantify
- Fast
- Reliable
- Affordable
CONS:
- May lack perspective over time
- Lacks perspective on actual browsing patterns, including subsequent page loads
- Lacks perspective on other locations
- May lack information on the source of an issue
TOOLS:
Live monitoring and alerts
The performance of a webpage can degrade very quickly. It can degrade due to poor network conditions, an influx of bot visitors from a new ad, a influx of legitimate visitors during peak hours, or from the deploy of a non-performant feature.
When the performance does degrade, you need to know immediately, so you can either roll back the deploy, or investigate the other factors that may be slowing down the site.
Notice that price isn't listed as a benefit (pro) or drawback (con) of live monitoring tools. You generally need to pay something to use these tools effectively, but usually that price is less than $100 a month, even on large sites.
PROS:
- Real-time notifications of performance changes
- Easy to quantify
- Can be configured to request from other locations
CONS:
- Limited information
- Lacks perspective over time
- May lack information on the source of an issue
- Fragile configuration can lead to false positives
TOOLS:
Full stack observability
Some tools offer insights into the full page lifecycle over time. These tools are constantly ingesting data from your website and compiling that data into configurable visualizations. They look at page load data on the client side and server side using highly granular measurements such as database transaction time, and the number of http requests.
If you wanted a single source of information to measure performance on your site, then these tools are the only option. They can provide one time assessments, monitoring, and backend insights.
One big problem with these tools is that they are quite complex. To use these tools effectively, you need developers to help set them up, and you need data analysts to extract the important information exposed by these tools.
PROS:
- Includes detailed breakdowns that can help identify the source of performance issues
- Includes data over time
- Highly granular data
CONS:
- Difficult to use
- Expensive
- Requires developer setup and configuration
TOOLS:
Qualitative user tests
The final approach for measuring the performance of a webpage is subjective. In other words, you must actually look at how your site performs when real people are using it. This can be as simple as opening a website on all your devices and trying to browse like a normal user, or you can set up time to interview real users and gather qualitative information about their experience.
I once worked at a company that required developers to attend in-person user tests every two weeks. This allowed every developer to see how users actually browsed and experienced their work. This may be overkill for most companies, but it's a perspective that can't be ignored.
PROS:
- No additional tools are necessary
- Exposes actual, real world user experience
- Exposes issues raised from real browsing patterns including subsequent page loads
CONS:
- It's easy to prematurely generalize from a small number of cases
- Can be expensive and difficult to do well
- Difficult to quantify
- Not timely
TOOLS:
- Web browsers on multiple devices
- Google Analytics
- User testing services
Conclusion
In this article, I introduced four different ways to measure the performance of a webpage. Each of them is necessary to get a full understanding of the performance of a page. I also introduced some of my favorite tools that can be used for each approach.
The four approaches are...
- One time assessments
- Live monitoring and alerts
- Full stack observability
- Subjective user tests
I hope that this prepares you to start wading into the complex world of measuring website performance.
Evan's React Interview Cheat Sheet
In this article I'm going to list some of the things that are useful in React coding interviews, but are easily forgotten or overlooked. Most of the techniques here are useful for solving problems that often arise in coding interviews.
Table of contents
- Imitating componentDidMount using hooks
- Using the previous state in the useState hook
- Using useRef to refer to an element
- Custom usePrevious hook to simplify a common useRef use case
- Vanilla js debounce method
- Use useContext to avoid prop-drilling
Imitating componentDidMount using React hooks
The useEffect hook is one of the more popular additions to the React hooks API. The problem with it is that it replaces a handful of critical lifecycle methods that were much easier to understand.
It's much easier to understand the meaning of "componentDidMount" than "useEffect({}, [])", especially when useEffect replaces componentDidMount, componentDidUpdate, and componentWillUnmount.
The most common use of the useEffect hook in interviews is to replace componentDidMount. The componentDidMount method is often used for loading whatever data is needed by the component. In fact, the reason it's called useEffect is because you are using a side effect.
The default behavior of useEffect is to run after ever render, but it can also be run conditionally using a second argument that triggers the effect when it changes.
In this example, we use the useEffect hook to load data from the omdb api.
// fetch movies. Notice the use of async
const fetchMovies = (newSearchTerm = searchTerm) => {
// See http://www.omdbapi.com/
fetch(`http://www.omdbapi.com/?apikey=${apikey}&s=${newSearchTerm}&page=${pageNumber}`).then(async (response) => {
const responseJson = await response.json();
// if this is a new search term, then replace the movies
if(newSearchTerm != previousSearchTerm) {
setMovies(responseJson.Search);
} else {
// if the search term is the same, then append the new page to the end of movies
setMovies([...movies, ...responseJson.Search]);
}
}).catch((error) => {
console.error(error);
});
};
// imitate componentDidMount
useEffect(fetchMovies, []);
Note that this syntax for using asynchronous code in useEffect is the suggested way to do so.
Using the previous state in the useState hook
The useState hook is probably the easiest and most natural hook, but it does obscure one common use case. For instance, do you know how to use the previous state in the useState hook?
It turns out that you can pass a function into the useState set method and that function can take the previous state as an argument.
Here's a simple counter example.
const [count, setCount] = useState({});
setCount(prevState => {
return prevState + 1;
});
Using useRef to refer to an element
The useRef hook is used to store any mutable value across calls to the component render method. The most common use is to use it to access an element in the DOM.
Initialize the reference using the useRef hook.
// use useRef hook to keep track of a specific element
const movieContainerRef = useRef();
Then attach it to an element in the render return.
<div className={MovieListStyles.movieContainer} ref={movieContainerRef}>
{movies && movies.length > 0 &&
movies.map(MovieItem)
}
</div>
Then you can use the .current property to access the current DOM element for that div, and attach listeners or do anything else you need to do with a div.
// set up a scroll handler
useEffect(() => {
const handleScroll = debounce(() => {
const scrollTop = movieContainerRef.current.scrollTop;
const scrollHeight = movieContainerRef.current.scrollHeight;
// do something with the scrolling properties here...
}, 150);
// add the handler to the movie container
movieContainerRef.current.addEventListener("scroll", handleScroll, { passive: true });
// remove the handler from the movie container
return () => movieContainerRef.current.removeEventListener("scroll", handleScroll);
}, []);
Custom usePrevious hook to simplify a common useRef use case
The useRef hook can be used to store any mutable value. So it's a great choice when you want to look at the previous value in a state variable. Unfortunately, the logic to do so is somewhat tortuous and can get repetitive. I prefer to use a custom usePrevious hook from usehooks.com.
import {useEffect, useRef} from 'react';
// See https://usehooks.com/usePrevious/
function usePrevious(value) {
// The ref object is a generic container whose current property is mutable ...
// ... and can hold any value, similar to an instance property on a class
const ref = useRef();
// Store current value in ref
useEffect(() => {
ref.current = value;
}, [value]); // Only re-run if value changes
// Return previous value (happens before update in useEffect above)
return ref.current;
}
export default usePrevious;
Using it is as simple as one extra line when setting up a functional component.
// use the useState hook to store the search term
const [searchTerm, setSearchTerm] = useState('orange');
// use custom usePrevious hook
const previousSearchTerm = usePrevious(searchTerm);
Vanilla js debounce method
Okay, this next one has nothing to do with React, except for the fact that it's a commonly needed helper method. Yes, I'm talking about "debounce". If you want to reduce the jittery quality of a user interface, but you still want to respond to actions by the user, then it's important to throttle the rate of events your code responds to. Debounce is the name of a method for doing this from the lodash library.
The debounce method waits a preset interval until after the last call to debounce to call a callback method. Effectively, it waits until it stops receiving events to call the callback. This is commonly needed when responding to scroll or mouse events.
The problem is that you don't want to install lodash in a coding interview just to use one method. So here's a vanilla javascript debounce method from Josh Comeau.
//
const debounce = (callback, wait) => {
let timeoutId = null;
return (...args) => {
window.clearTimeout(timeoutId);
timeoutId = window.setTimeout(() => {
callback.apply(null, args);
}, wait);
};
}
export default debounce;
Here's an example of how to use it to update a movie list when a new search term is entered.
// handle text input
const handleSearchChange = debounce((event) => {
setSearchTerm(event.target.value);
fetchMovies(event.target.value);
}, 150);
return (
<div className={MovieListStyles.movieList}>
<h2>Movie List</h2>
<div className={MovieListStyles.searchTermContainer}>
<label>
Search:
<input type="text" defaultValue={searchTerm} onChange={handleSearchChange} />
</label>
</div>
<div
className={MovieListStyles.movieContainer}
ref={movieContainerRef}
>
{movies && movies.length > 0 &&
movies.map(MovieItem)
}
</div>
</div>
);
Use useContext to avoid prop-drilling
The last thing an interviewer wants to see during a React coding interview is prop drilling. Prop drilling occurs when you need to pass a piece of data from one parent component, through several intervening components, to a child component. Prop drilling results in a bunch of repeated code where we are piping a variable through many unrelated components.
To avoid prop drilling, you should use the useContext hook.
The useContext hook is a React implementation of the provider pattern. The provider pattern is a way of providing system wide access to some resource.
It takes three code changes to implement the useContext hook. You've got to call createContext in the parent component that will maintain the data. Then you've got to wrap your app with a special tag.
export const DataContext = React.createContext()
function App() {
const data = { ... }
return (
<div>
<DataContext.Provider value={data}>
<SideBar />
<Content />
</DataContext.Provider>
</div>
)
}
Then you've got to import the context in the child component, and call useContext to get the current value.
import DataContext from '../app.js';
...
const { data } = React.useContext(DataContext);
What has made you stumble in React interviews?
What are some other common ways to make mistakes in React coding interviews? Send me your biggest React coding headaches on Twitter @EvanXMerz.
Why to stick with Heroku, or make the switch
One question I hear a lot lately is when a company should stick with Heroku or switch to something else. In this article I'm going to lay out the pros and cons for Heroku, and compare it with the typical alternative, AWS.
Three reasons to stick with Heroku
Don't fall victim to thinking that "the grass is always greener on the other side." That hot new technology on AWS or Azure may look cool now, but Heroku offers a lot of great features that should fit the bill for many growing companies.
Heroku supports easy scaling with sticky sessions
Horizontally scaling any web application is hard. In a traditional web app, you must optimize your code so that it runs in parallel. You must deal with race conditions that arise when multiple servers are trying to interact with a shared resource such as a cache or a database. You must find a way to balance load across multiple servers while sharing state across all instances.
Session data is visitor-specific data that is stored on the server. If a visitors's session is stored on one server, but their request is routed to another server, then that server won't know about anything they've done in the current session. It may not know if they're logged in or not. It may not have the browsing filters that they've configured.
Session affinity, also known as sticky sessions, is one solution to the problem of sharing session across servers. With session affinity enabled, all of a visitor's requests will always be routed to the same instance. There are some drawbacks to session affinity, but the benefit of being able to scale horizontally before having to tackle some parallelization problems may outweigh the drawbacks in your use case.
Many services offer session affinity, but none are as easy to set up as Heroku. With literally two clicks you can enable session affinity and start scaling out. Don't let hype distract you from an approach that may reap benefits for your web property.
Heroku provides zero down time deploys and upgrades
One of the best features of Heroku is that their technology and support teams handle deploys and upgrades. They make it so that your dev team doesn't have to worry about keeping the servers up during deploys or running migrations during upgrades.
The preboot feature allows you to keep the old version of your app running during a deploy. This means that users will only ever be routed to a server that has booted up and is running your app, and that means that you aren't turning away customers in that 30 second window where the new version of your app is loading.
Heroku also supports seamless upgrades for the most popular add-ons, such as REDIS. When I switched my company from Heroku REDIS to AWS REDIS we were surprised that our site went down a few weeks after the switch. AWS may force upgrade your technology without providing a way to seamlessly switch to the new version. So AWS forces you to track each upcoming patch and ensure that your team is ready for the switch.
Heroku is cheaper than the alternative because anyone can use it
Heroku seems very expensive when the bills come due, but in my experience it's cheaper than the alternative. Heroku is so easy to use that anyone can use it. With a few clicks or commands a backend developer can enable sticky sessions. With a few clicks or commands they can enable preboot. With a few clicks or commands they can add REDIS or PostGreSQL or any of the many add-ons provided by Heroku.
To use all those different products on a less managed product, such as AWS or Azure, you must retain a dedicated DevOps specialist. These people have very specialized skills and are not cheap. In my experience, using Heroku saves the cost of around one expensive employee. So as long as your Heroku bill is less than the cost of one employee, it's probably the more affordable option.
Three reasons to switch
There are many good reasons to stick with Heroku, but it certainly has limits. Here are the reasons why I've moved services from Heroku to somewhere else in the past.
Heroku is dangerous because anyone can use it
When you're on Heroku, you may not need to hire dedicated staff to manage your web infrastructure. This is a significant cost savings, but it means that the DevOps tasks are going to be offloaded on your web programmers. So you must ensure that you hire the skills on your team to understand Heroku. Heroku may not be as complex as AWS, but it still requires a foundational understanding of how the web works, linux, and logging. If your programmers are exploring the features in Heroku without the requisite experience or training then they may make mistakes that harm your business.
Heroku offers fewer options for international support
Heroku lacks flexible support for internationalized websites. As it says in the regions documentation, each "Private Space exists in a single region, and all applications in the Private Space run in that region.". This may sound confusing, but it ultimately means that each project in Heroku can only run in one region. If you want to support another region, then it must be in a separate project and hosted at a separate domain or subdomain. So if you want to internationalize your website using subdirectories, which is advantageous because it inherits the existing search reputation of your domain, then you can't do that with geographically distributed servers on Heroku.
Heroku offers fewer vertical scaling options
Heroku dynos come in six different flavors as of this writing. If you need anything outside of those six options then you are out of luck. The beefiest dyno is the performance-l machine, which offers 14Gb of RAM. If you need more than that, then you need to switch to another platform. What if you're using very little memory, but you want to use many CPU cores? The only option is to pay for the most expensive dynos. This lack of flexibility means that if your web service is a pretty standard website or API, then Heroku probably won't serve your needs very well.
How to decide whether to stick with Heroku or to switch
In this article I've listed some reasons to stick with Heroku and some reasons to switch, but the final decision is largely dependent on your use case. If you are making a pretty standard website or API, then Heroku is probably fine even when horizontally scaling. There are three main scenarios where you should strongly consider switching to a more complex cloud hosting service.
- Your service need more flexible options for internationalization
- Your service doesn't match the requirements common to most web apps and APIs
- Your service is scaling exponentially
Why is software development so slow?
Why can't the developers move more quickly? Why does every project take so long? In this article I'm going to talk about what causes software development to slow down. This is based on my experience as a programmer and manager in Silicon Valley for over a decade, as well as conversations with other people in similar roles.
Is my team slow because...
First of all, I want to answer some questions that I've heard from managers.
- Is my team slow because we use PHP? No.
- Is my team slow because we use Java? No.
- Is my team slow because we use INSERT PRODUCT HERE? Probably not.
- Is my team slow because they don't understand web 3? No.
- Is my team slow because of a bad manager? Maybe, but if you've had multiple managers in there who haven't moved faster then this is unlikely.
- Is my team slow because they have or do not have college degrees? No.
- Is my team slow because we don't use agile? Probably not.
So then why is my development team so slow?
Now that we've gotten that out of the way, we can drill into what actually causes teams to move slowly. None of the reasons I've listed here are particularly technical. I tend to think that teams move slowly due to non-technical reasons.
They're estimating poorly
The most common reason why people think developers are moving slowly is that the estimates are poor. For one reason or another, developers are not spending the time, or creating the artifacts necessary to generate accurate estimates.
I once joined a small team where nobody ever wrote anything down. They would attend planning meetings where they would hash out the requirements, they would make up an estimate on the spot, then they would immediately break to start writing code. The stakeholders wanted to know why tasks never seemed to come in within the estimated time frame.
In my experience, it's necessary to generate multiple levels of plans to generate an accurate software development project estimate. You must begin with use-cases, then work through multiple levels of plans to reach units of work that can be estimated.
Here's how I break down a project to make an accurate estimate:
- Write all requirements down in plain language. This can be in the form of use cases, or user stories, or any top-level form that anyone can understand.
- Talk through what an acceptable solution would look like with other developers.
- Break that solution into individual tasks using a tool like JIRA.
- Estimate those tasks.
- Add a little extra room for unknowns, debugging, integration, and testing.
- Add up the final estimate.
There are multiple ways to execute each of these steps, but that's the general flow for all successful software development estimation that I've seen in my career.
Technical debt
Another big reason why a team might be moving slowly is tech debt. Tech debt happens to every project when the company tries to move quickly. It happens because solutions that can be implemented as fast as possible are usually not scalable to future use cases that the developers can't anticipate.
I once worked on a web development team that needed to make the list pages faster on an e-commerce website. The list pages were slow because the server was delivering a massive payload containing every product on the list page then filtering and sorting was done client side. That's easy to fix right? Just serve the first page of products, then serve the next when the user scrolls down. But to do that, we must add server side sorting and filtering because the list will be different for different combinations of sorts and filters. But to do that, we must have products stored in some sort of database, and this team was only using a key-value store at the time. That's how tech debt can be problematic. In that scenario, it would take months to solve that problem in a scalable way.
Ultimately, we found a different solution. We delivered the tiniest possible payload for each product, which achieved a similar end goal while side-stepping the tech debt. But that tech debt was still there, and would still be a problem for many related projects.
There is no silver bullet for solving tech debt. In my career, the best way I've found to deal with tech debt is to reserve one developer each sprint to iteratively move the project closer to what the team agrees is a more scalable solution.
Interpersonal conflict
The single biggest thing that slows down development teams is interpersonal and interdepartmental conflict. Maybe a manager is at odds with a team member, or maybe two team members see their roles very differently. If two people working on one project have different views about how to execute that project it can be absolutely crushing.
I think that proactively avoiding and addressing conflict is the single thing that can instantly help teams move faster. The most staggeringly successful group of techniques I've discovered for doing so is called motivational interviewing.
Motivational interviewing is a way of communicating with peers, stakeholders, and direct reports that allows two people to agree to be on the same team. It's a way of building real rapport with people that allows you to build successful working relationships over time. Specifically, it's a way of focusing a conversation on talk that meaningfully moves both parties toward a mutually acceptable solution.
I strongly recommend Motivational Interviewing for Leadership by Wilcox, Kersh, and Jenkins. To say that that book changed my life is an understatement.
Unclear instructions
The final category of issue that slows down development teams is unclear instructions.
I once worked for a person who was very fond of sending short project descriptions over email and expecting fast results. One email might read something like "I want to be selling in Europe in one month", another might say "I want to be able to hold auctions for some of our products."
It IS possible to work with such minimal instructions. It falls upon the development team to outline what they think the stakeholder wants, develop plans based on that outline, then execute based on those plans.
The problem with working from minimal instructions is that no two people see the same problem in exactly the same way. Say you are developing that auction software. Many questions immediately arise. Who can bid on the auctions? Is the auction for many products or just one? Does the product have selectable options, or are they preset? Are bids visible to other customers? How do we charge the user for the item if the final bid exceeds what is typically put on a credit card?
If the stakeholder isn't able to answer those questions early in the process, then it may go significantly astray. If the development team doesn't understand the instructions, then they may waste days or weeks pursuing irrelevant or untimely work. If the stakeholder is going to give such vague instructions then they must address questions in a timely manner or risk tasting time.
In conclusion
So why is your development team so slow? I've laid out the four common causes that I've seen in my career.
- Poor estimates
- Tech debt
- Interpersonal conflict
- Unclear instructions
If you have something else to add to this list, then let me know on Twitter @EvanXMerz.
How to generate and view a node bill of materials
In this post, I'm going to show you how you can generate and view a software bill of materials (sbom) for a node.js project.
Why generate a bill of materials?
With the increasing attacks on the software supply chain by malicious actors such as foreign states and crypto miners, it's important to understand exactly what packages are in your node project. It's also important to understand the vulnerabilities in each of those packages. The tools in this tutorial link out to Socket.dev, which analyzes npm packages to find potential vulnerabilities. You may also want to consult with the National Vulnerability Database to see if any of your packages have known security holes.
Steps
- Install cyclonedx/bom. The CycloneDX SBOM Specification is a format for listing packages in a node library and any other software project. cyclonedx/bom is a tool for generating a sbom that conforms to the CycloneDX SBOM Specification.
npm install -g cyclonedx/bom
- Use npx to run it. Navigate to the directory of your node project and run the following. This tells the package to generate a bill of materials by analyzing the node_modules folder in the current folder and save the output to bom.json.
npx @cyclonedx/bom . -o bom.json
- Install viewbom. Viewbom is a simple npx tool I wrote that generates an html UI for navigating the bill of materials you just generated.
npm install -g viewbom
- Run viewbom on the sbom you created.
npx viewbom bom.json bom.html
- Open bom.html in a web browser. This should present you with a simple UI that shows you some basic statistics about your sbom and give you a way to search the packages in it.
It should look something like the following screenshot.
Introduction to HTML
In this post, I'm going to introduce HTML, and demonstrate how it acts as the skeleton for all pages on the internet.
TLDR: HTML defines the structure of the page
HTML is used to define the structure of a web page. It's the structure that your styles (css), and code (JavaScript) sits on top of. The CSS styles the html elements. The code allows interaction with html elements.
What is HTML?
HTML stands for Hyper Text Markup Language. The unusual phrase "hyper text" means "more than text". Hyper text can be contrasted to plain text. You read plain text in a printed book. The words string together to form a story. But what if you want to know more about a topic? What if you want a definition for a term? What if you want to email the author? HTML gives web programmers the ability to "mark up" the text in a way that allows web browsers to add additional functionality to a text document.
HTML is one of the key inventions that enabled the web to become so popular. The ability to add cross-references directly in a document enables all sorts of connections and interactions that were formerly impossible. Nearly every page on the internet is made using HTML.
What are HTML elements?
The classic example of a hyper text element is a link. When links were invented, they were called "anchors", as if the author is anchoring a bit of text to another document. When you want to add a link to an html document you use the anchor or "a" element. Here's an example.
<a href='https://www.myblog.com'>My blog</a>
Wow, that's pretty confusing, isn't it? But take a moment to look more closely. What parts of it do you see?
An html element can have an opening tag, any number of attributes, content, and a closing tag, as shown in this diagram.
Here's another example of an html element. It's the image, or "i" element.
<img src="https://www.evanxmerz.com/images/AnchorTag_01.jpg" alt="Anchor tag diagram"/>
Notice that the image element shown here doesn't have a closing tag. This is the shortened form of an html element that doesn't need any content. It ends with "/>" and omits the content and closing tag.
The structure of an html document
HTML documents are composed of numerous html elements in a text file. The HTML elements are used to lay out the page in a way that the web browser can understand.
<!DOCTYPE html>
This must be the first line in every modern html document. It tells web browsers that the document is an html document.
<HTML>
The html element tells the browser where the HTML begins and ends.
<HEAD>
The head element tells the browser about what other resources are needed by this document. This includes styles, javascript files, fonts, and more.
<BODY>
The body element contains the content that will actually be shown to a user. This is where you put the stuff that you want to show to the user.
An example html document
Here's an example html document that contains all of the basic elements that are required to lay out a simple HTML document.
I want you to do something strange with this example. I want you to duplicate it by creating an html document on your computer and typing in the text in this example. Don't copy-paste it. When you copy-paste text, you are only learning how to copy-paste. That won't help you learn to be a programmer. Type this example into a new file. This helps you remember the code and gets you accustomed to actually being a programmer.
What about typos though? Wouldn't it be easier and faster to copy-paste? Yes, it would be easier and faster to copy-paste, however, typos are a natural part of the programming experience. Learning the patience required to hunt down typos is part of the journey.
Here are step-by-step instructions for how to do this exercise on a Windows computer.
- Open Windows Explorer
- Navigate to a folder for your html exercises
- Right click within the folder.
- Hover on "New"
- Click "Text Document"
- Rename the file to "hello_world.html"
- Right click on the new file.
- Hover over "Open With"
- Click Visual Studio Code. You must have installed Visual Studio Code to do this.
Now type the following text into the document.
<!DOCTYPE html>
<html>
<head>
<title>Hello, World in HTML</title>
</head>
<body>
Hello, World!
</body>
</html>
Congratulations, you just wrote your first web page!
Save it and double click it in Windows Explorer. It should open in Google Chrome, and look something like this.
There are many HTML elements
There are all sorts of different html elements. There's the "p" element for holding a paragraph of text, the "br" element for making line breaks, and the "style" element for incorporating inline css styles. Later in this text we will study some additional elements. For making basic web pages, we're going to focus on two elements: "div" and "span".
If you want to dig deeper into some other useful HTML elements, see my post on other useful html elements.
What is a div? What is a span?
That first example is pretty artificial. The body of the page only contains the text "Hello, World!". In a typical web page, there would be additional html elements in the body that structure the content of the page. This is where the div and span elements come in handy!
In the "div" element, the "div" stands for Content Division. Each div element should contain something on the page that should be separated from other things on the page. So if you had a menu on the page, you might put that in a div, then you might put the blog post content into a separate div.
The key thing to remember about the "div" element is that it is displayed in block style by default. This means that each div element creates a separate block of content from the others, typically by using a line break.
<div>This is some page content.</div>
<div>This is a menu or something.</div>
The "span" element differs from the div element in that it doesn't create a new section. The span element can be used to wrap elements that should be next to each other.
<span>This element</span> should be on the same line as <span>this element</span>.
An example will make this more clear.
Another example html document
Again, I want you to type this into a new html file on your computer. Call the file "divs_and_spans.html". Copy-pasting it won't help you learn.
<!DOCTYPE html>
<html>
<head>
<title>Divs and spans</title>
</head>
<body>
<div>
This is a div and <span>this is a span</span>.
</div>
<div>
This is another div and <span>this is another span</span>.
</div>
</body>
</html>
Here's what your page should look like.
It's not very exciting, is it? But most modern webpages are built using primarily the elements I've introduced to you on this page. The power of html isn't so much in the expressivity of the elements, but in how they can be combined in unique, interesting, and hierarchical ways to create new structures.
Summary
In this section I introduced Hyper Text Markup Language, aka HTML. Here are the key points to remember.
- HTML is the skeleton of a web page.
- HTML elements usually have opening and closing tags, as well as attributes.
- HTML documents use the head and body elements to structure the page.
- Div and span elements can be used to structure the body of your webpage.
More
Introduction to CSS
In this post, I'm going to introduce CSS, and demonstrate how it can be used to apply styles to a web page.
TLDR: CSS defines the look and feel of a web page
After building the structure of a page using HTML, you can add styles to HTML elements using CSS. If HTML is a car's frame, then CSS is the body shape, the paint job, and the rims.
What is CSS?
CSS stands for Cascading Style Sheets. Programmers and web designers use CSS to describe the colors, fonts, and positions of HTML elements. Web browsers interpret the CSS on a page to show those styles to viewers.
CSS can be added to an HTML document in multiple ways. It can be added directly to HTML elements using inline styles, it can be added to the head section using style elements, or it can be included from another file.
A simple inline css example
Here's an example of a very simple use of inline css using the style attribute on an element. All it does is set the font color to blue.
<div style="color: blue;">This text should be blue.</div>
To try this in your own html file, just type that code within the body section of the page. If you did it right, then the text should appear blue.
There are three important parts of this inline css. The first is the style attribute on the div element. Use the style attribute to apply inline styles. The second is the word "color", which specifies which property of the element that should be styled. The third important bit is the word "blue", which specifies the color. There are many ways to specify colors in CSS, and using names is the simplest.
I'm introducing inline styles here because they are useful for debugging, however, inline styles are impractical for a number of reasons, so we're going to look at a few other ways of incorporating CSS into an HTML document.
A simple CSS style element example
CSS styles can also be included in a document using the HTML style element. Usually such styles are placed in the head section of the document.
Using the HTML style element like this is generally discouraged, however, because the styles are not reusable in other documents. That's why styles are usually stored in separate files that can be included in any HTML document.
It's good to know about style elements, however, because they are sometimes used in emails. When creating a beautiful email, you don't have access to other files, so style elements are sometimes the best option.
<style>
span {
background-color: red;
}
</style>
This CSS rule sets the background color to red for all span elements on a page. Here's how it might look in a full HTML document.
<!DOCTYPE html>
<html>
<head>
<title>Divs and spans</title>
<style>
span {
background-color: red;
}
</style>
</head>
<body>
<div>
This is a div and <span>this is a span</span>.
</div>
<div>
Notice that <span>all span elements on the page receive the style indicated in the rule above</span>. This is due to how CSS selectors work.
</div>
</body>
</html>
Here's a graphic showing how that style breaks down into parts. The selector is used to select which elements will recieve the style. The property and value describe the style to apply, just like when using inline styles.
What are selectors?
The problem with inline CSS is that it clutters up the code for your webpage, and it's not reusable. A single style declaration can be twenty or more lines long. That's a lot to insert into an HTML element, and adding it makes the code difficult to read. Also, inline styles only apply to the element they are on. If you want to use an inline style twice, then you have to duplicate it, which is a big no-no in programming. Fortunately, there are other ways to write CSS.
CSS can be included in a "style" HTML element. CSS written in the style element uses syntax called "selectors" to apply styles to specific elements on a page.
For example, say you wanted all span elements on your page to have a larger font. You might include something like the following in the head section of your HTML document.
<style>
span {
font-size: 2em;
}
</style>
That selector selects all elements with a given tag name. You could use "div" to style all divs, or "h1" to style all h1 elements. But what if you want to be more specific what if you only want to style some elements of a given type?
If you want to be more specific, then you can add class or id attributes to your html, then use those attributes in your CSS selectors.
Here's an example that shows a class attribute on a div and a selector that uses the class value. Notice that the class name is preceded by a period in the selector.
<style>
div.blueText {
color: blue;
}
</style>
<div class="blueText">This text should be blue</div>
Here's an example that shows how to use the id attribute and selector. Notice that the hashtag symbol precedes the id in the css selector
<style>
#blueText {
color: blue;
}
</style>
<div id="blueText">This text should be blue</div>
Usually class is used when you want to have many things that share the same style. The id attribute is used when you are styling something that is unique on the page.
Including CSS in a separate file
Inline styles and style elements are not the most common way of including styles in a web page. Usually styles are imported from a stylesheet file that contains CSS and ends with the .css extension.
Type the following code into a file named css_example.css.
span.blue {
color: blue;
}
span.red {
color: red;
}
#title {
font-size: 2rem;
}
Now type the following code into a file named css_example.html.
<!DOCTYPE html>
<html>
<head>
<title>CSS Example</title>
<link rel="stylesheet" href="css_example.css">
</head>
<body>
<h1 id="title">This is a large title</h1>
<div>
This is a div with <span class="red">red text</span>.
</div>
<div>
This is another div with <span class="blue">blue text</span>.
</div>
</body>
</html>
If you did it all correctly, then you should end up with something like this, when opened in a web browser.
What are all the CSS properties?
CSS can be used to style just about any aspect of an HTML element. With CSS you can create shadows, animations, and nearly anything else you see on the web. In my posts I'm going to focus on the most common CSS properties, but if you want to see the complete list of things that can be styled with CSS, then check out MDN's comprehensive list of CSS properties.
Summary
- CSS is the style layer of the web.
- CSS is usually imported into an HTML file from a separate CSS file.
- The element, class, and id selectors allow you to target your styles to specific elements.
More
CSS is quite large and complex, but you don't have to know everything about it to use it effectively. You only need to know the fundamentals, and be able to use web search to find the other stuff.
Project: Personal web page
This post contains the first assignment in my free web programming course.
Project: Personal web page
Now that you know a little html and css, you should be able to produce some simple web pages. In this assignment, I want you to create a web page about yourself. It should be pretty similar in content to my about-me page on my website, but it should be about you and the things you like.
Here's what it must contain.
- A title that says your name and is styled like a title.
- A picture of you or something you like.
- A short paragraph of at least three sentences. You can use Lorem Ipsum if you can't write three sentences about yourself.
- A quote you like that is styled like a quotation. Usually quotes are indented more and displayed in italics.
- At least three links to things you've done or things you like. Use html lists to show these.
Other useful HTML elements
In this post, I'm going to introduce some HTML elements that are still useful in the modern web development, even if they aren't used as often as div or span.
TLDR: These HTML elements are useful for accessibility and search engine optimization
You can build most webpages using only div and span elements, along with the structural elements html, head, and body. However, it's important to know about other elements if you want your page to rank highly in Google search results, or you want your page to be accessible to people with disabilities.
Headings and titles
Heading elements are used to show titles or subtitles. They are important to your visitors because they make text more readable. They are important to Google because Google uses them to understand the content of your pages.
Heading elements are h1, h2, h3, h4, and h5. The h1 element is the top level title of the page, and should only occur once on a page, otherwise Google Search Console will show a warning for that page, and you won't rank as highly in web searches.
Here's an example of an h1 element.
<h1>I'm a page title that can be seen by the visitor</h1>
It's important to note that there IS an actual title element in HTML. When the title element appears in the header, it is used to set the text that is shown in the tab of your web browser.
<title>This is the text at the top of the tab</title>
Paragraphs in HTML
Before CSS was invented and standardized, there were lots of HTML elements used to format text. You can still use some of these, but it is widely discouraged. The "b" element bolds the enclosed text. The "i" element applies italics to the enclosed text. The "br" element adds a newline. You should never use any of those elements, but there is one element that is still quite useful.
The "p" tag is used to enclose a paragraph of text. It is useful as something separate from a div because it includes useful browser default styles, and because it serves as a good way to style the body text for your page.
<p>I'm a paragraph with some nice browser default styles.</p>
Browser defaults are a complex and controversial topic. Each browser starts with its own set of default styles so that a basic HTML page will be readable. There are many modern packages that override or eliminate these styles so that they don't interfere with a site's custom styles. I don't have a strong opinion on browser defaults in either direction.
Lists in HTML
Lists are another bit of semantic HTML that are still widely used. There are two types of lists in HTML, ordered lists and unordered lists. These can be represented by the "ol" and "ul" elements respectively. List items can be represented by the "li" element.
So what's the difference between the two list types? Ordered lists are numbered, while unordered lists are bulleted.
<ol>
<li>First item</li>
<li>Second item</li>
</ol>
If you drop that into an HTML file then you will see something like the following.
I encourage you to replace the "ol" elements with "ul" and see what happens.
Nav, main, and footer elements in HTML
Finally, I want to recommend using the elements nav, main, and footer. These elements are used to encapsulate the navigation portion of the page, the main content of the page, and the footer of the page. These elements are absolutely VITAL for accessibility, as they tell screen readers about which part of the page they are reading. You need to remember accessibility for two reasons. It is important to allow as many people as possible to access your site, and Google will penalize you if you don't.
They are used just like divs.
<nav>
<ul>
<li>Menu item 1</li>
<li>Menu item 2</li>
<li>etc</li>
</ul>
</nav>
Introduction to JavaScript
In this post, I'm going to introduce the JavaScript programming language and show you a few ways it's commonly used on the web.
WARNING: Some prior programming required!
I'm not introducing programming in general. This isn't a good first tutorial for someone who has never written a line of code in their life. I'm assuming that you know what functions are, and that methods and subroutines are the same thing. I'm assuming that you've written at least a little of some kind of code in the past.
TLDR: JavaScript is the engine of a webpage
HTML is used to define the structure of a web page. CSS defines the look and feel of a page. JavaScript defines how a page acts and responds to user input.
If a web page is a car, then HTML is the frame, CSS is the body, and JavaScript is the engine.
A haiku about JavaScript
Dearest Javascript,
You are my favorite tool
that costs me nothing.
Why not Java?
JavaScript has nothing to do with Java. The name JavaScript came about as an attempt to steal some of the hype around the Java programming language in the 1990s. In the 30 years since then, their positions have entirely flipped, with JavaScript now being the programming language that sees tons of innovation and market support, while Java is withering. Why has this happened? Why has JavaScript been so successful?
In my opinion, Java has been uniquely unsuccessful as a language because it has refused to grow and innovate. Java set some standards and some conventions for the language, and even thirty years later, the committees who guide specification for that language continue to be far too conservative.
If you're a Java programmer and you don't agree with me, then I encourage you to try Microsoft C#. That's the fun language that Java could have been.
And why is that?
C# and JavaScript have become incredibly open-ended. You can write code in lots of different ways, using features borrowed from other cool languages and frameworks. Both are sort of like Frankenstein's monster inasmuch as they are collections of mismatched parts that come together to form a surprisingly intimidating beast.
And this scares some people.
But, much like Shelley's actual monster, they are actually quite nice, and I hope to convey that to you in this introduction.
But what is JavaScript?
It's a programming language that runs in the web browser and on nearly every device that exists. On a webpage, you include JavaScript within the script element, or by including code in an external file. There's more to it than that, but let's learn by doing.
A JavaScript function
One of the key responsibilities of JavaScript is to respond to user input. Beside a few basic interactions, like clicking a link or a dropdown, every action that occurs on a webpage is handled by custom event handlers that are usually written in JavaScript. In this case, we're going to write a script that handles a click event.
When the user clicks on something, the browser creates what is called a click event. It then runs a bit of code called a function that is tied to that event. Here's the click handler function that we're going to write.
function showWarningOnClick() {
alert("Warning! The elders of the internet have been notified!");
}
That might look like nonsense to you, so let's break it down. This graphic shows how each of the pieces come together.
The first line is the function declaration. The word "function" declares that the next word is the name of a function. So "showWarningOnClick" is the name of the function. The parentheses, "()", would enclose the method arguments, if we needed any. The open bracket, "{", indicates that the next line will be the first line of the function body.
The second line is the actual code that is executed by this function. In this case, it calls another function. It calls "alert", with the argument "Warning! The elders of the internet have been notified!". Notice that the argument is enclosed with parentheses. Then the line ends with a semicolon. Most lines of JavaScript code must end in a semicolon.
That's the code that we will run when a button is clicked. We will see in the next section how we can trigger that function using a button.
A JavaScript event
The following line of code creates a button and adds a handler for the onClick event.
<button onClick="showWarningOnClick();">Don't click me</button>
Notice that the button isn't created using a div or span element, as you might expect. When creating buttons, it's good to use the button element if you can. This element is friendly to screen readers, and other accessibility technology.
The only attribute given is "onClick", and it's set to call the function that we defined earlier. The onClick attribute tells the browser what to do when the button is clicked.
It's important to note that onClick events can be attached to any elements. They aren't something unique to buttons. You can attach methods to the onClick attributes of divs or spans, or any other HTML elements.
Responding to a click event in JavaScript
So let's put it all together. The code should look something like this. And as a reminder, please type this in, don't copy paste it. The point is to learn, and you will learn and remember more if you type it out.
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Hello World</title>
<script>
function showWarningOnClick() {
alert("Warning! The elders of the internet have been notified!");
}
</script>
</head>
<body>
<div>
<button onClick="showWarningOnClick();">Don't click me</button>
</div>
</body>
</html>
When you run it, by double clicking it and opening it in a web browser, you should see a single button in the upper left that says "DOn't click me". Click it, and you should see a little popup with the message "Warning! The elders of the internet have been notified!".
Summary
JavaScipt is a fun, open-ended programming language that is particularly useful in web browsers. In this post we learned about JavaScript functions and event handlers.
More
Here are some other introductions to JavaScript from around the web. I suggest that you spend time to check them out, even if you found this really easy. It's good to go over the fundamentals often, and it's particularly good to get multiple perspectives.
- MDN introduction to JavaScript. I link to MDN in every post because it's the most reliable source.
- Introduction to JavaScript from javascript.info.
- Introduction to JavaScript from freecodecamp. Some of what he says in this video is a bit misleading, but it's still a good place to start.
What is the DOM?
In this post, I'm going to introduce the Document Object Model, which is better known as the DOM.
TLDR: The DOM is the way a webpage is represented within a browser
The DOM is a bit of structured data that holds a representation of a webpage in order to enable programs to be able to manipulate it.
It's a bit like a 3d model that you might use in a 3d printer. The 3d printer is the display device, like a web browser. The 3d model is needed by the 3d printer for it to be able to do anything. You can manipulate the 3d model to make the 3d printer do what you want.
Why should we care about the DOM?
Why does the DOM matter? Can't we go about writing web code without knowing about the DOM?
The DOM matters because it is the representation of a web page that we can work with in JavaScript. It's true that, as a web programmer of over twenty years now, I have probably explicitly thought about the DOM only a handful of times. But I use it every day, and in the dark ages of JavaScript, we had to work with it very directly. These days we use packages like React or jQuery to wrap add a convenient layer of abstraction above the DOM. Still, in this post I'm going to show you how to use the DOM the old fashioned way.
This is still very important to know because occasionally you do have to do it, even when using the modern libraries for interacting with the DOM.
Finding elements in the DOM in JavaScript
In JavaScript, the DOM is stored as a global variable named "document". So when you want to do something with the DOM, you use the document variable.
One of the things you must be able to do is find the HTML element that you want to work with. The document gives us several handy methods to find elements by their id or class attributes. The getElementById method returns the first element found with the matching id attribute. Since id attributes should be unique, this should be the only element with that id in the document.
So to find a document with the id "elephant", you'd use the following line of code.
let elephantElement = document.getElementById("elephant");
This line of code contains a few things that might look unfamiliar to anyone who hasn't used JavaScript before. Here's a diagram that breaks it down.
The word "let" is a reserved word that indicates that the next word is a locally scoped variable. Don't worry if you don't know what "locally scoped" means. In this case, it just means that it's not a global variable, like document. Then the word "elephantElement" is the variable name, and the equals sign indicates that whatever is returned by the expression on the right should be stored in the variable on the left.
The id attribute isn't the only way to find elements. JavaScript also specifies a way to get elements with the same class attribute using the getElementsByClassName method. Notice that "Elements" is plural because the method returns an array, as many elements may share a class.
let elephantElements = document.getElementsByClassName("elephant");
The structure of this line of code is essentially the same as the previous one. The only difference is the name of the method that is called, the name of the variable, and the type of the variable.
How to modify a webpage using JavaScript
You can also use JavaScript to change the content of a webpage. When you've located the element you want to change, you can use the innerHTML method to get or set the content of that element. Like much of the JavaScript in this post, this is considered a bad thing to do these days. Typically you'd use the methods in a UI framework like React to mutate the page.
let elephantElement = document.getElementById("elephant");
elephantElement.innerHTML = "Is this an elephant?"
You can also add elements to the page using the createElement and append methods. This blurb appends a copyright notice to a blog post.
let blogPost = document.getElementById("post");
let copyrightNotice = document.createElement("p")
copyrightNotice.innerHTML = "Copyright " + new Date().getFullYear() + " by the author";
blogPost.append(copyrightNotice);
Let's put that into a real HTML file to see it in action. Here's the full example. If you've typed it in correctly, then you should see a copyright notice at the bottom of a paragraph of latin.
<!DOCTYPE html>
<html>
<head>
<title>How to modify the DOM using JavaScript</title>
</head>
<body>
<div id="post">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
<script>
let blogPost = document.getElementById("post");
let copyrightNotice = document.createElement("p")
copyrightNotice.innerHTML = "Copyright " + new Date().getFullYear() + " by the author";
blogPost.append(copyrightNotice);
</script>
</body>
</html>
Summary
The DOM is the internal model of a webpage stored in the web browser. It can be manipulated using JavaScript methods such as getElementById, innerHTML, and append.
More
How to Debug a webpage in Chrome
In this post, I'm going to show you several ways to debug javascript in Google Chrome.
TLDR: Shift + ctrl + c
Open the javascript console using the shortcut shift + ctrl + c. That means, press all three at the same time. If you're on a Mac, use cmd rather than ctrl.
Inspecting an element to add new styles
Open the dom example that we wrote in What is the DOM?. In this post, we'll use that page to explore three ways to find out what's going on in a webpage. The code should look something like this.
<!DOCTYPE html>
<html>
<head>
<title>How to modify the DOM using JavaScript</title>
</head>
<body>
<div id="post">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
<script>
let blogPost = document.getElementById("post");
let copyrightNotice = document.createElement("p")
copyrightNotice.innerHTML = "Copyright " + new Date().getFullYear() + " by the author";
blogPost.append(copyrightNotice);
</script>
</body>
</html>
After opening it in Chrome, right click on the copyright notice, then click "Inspect" in the popup menu. The html for the element you clicked should appear in the "Elements" tab of the browser console. It should look something like this but there are several places where the console can appear.
Any time you are confused about an element, inspecting it is a good place to start. In the browser console, you should be able to see the html for the element, as well as any attached css. In fact, you can actually add new styles using the browser console. Notice in the "Styles" tab where it says "element.style" and there are empty braces. Any CSS you type in there will be applied to the selected element. Styles can also be toggled on and off. This can be exceptionally handy when trying to figure out why your page doesn't look quite right.
Go ahead and try it. Enter "font-weight: 800" into element.style. What happens?
Debugging using console.log
The most basic way to debug JavaScript is to put some console.log statements into your code, then open the console and see what they print. Calling console.log tells the browser to print whatever you pass in in the JavaScript console. You can console.log just about anything, but the web browser will format variables automatically if you put them in an object using curly braces.
So the following console.log call isn't very useful.
console.log('HERE!');
Whereas the following console.log call will allow you to examine the object passed int.
console.log({ confusingVariable });
Add the following line of code to the dom example after it sets the copyrightNotice variable.
console.log({ copyrightNotice });
Then reload the page and use shift + ctrl + c to open the browser console, or shift + cmd + c on a Mac. You may have to expand the window and click over to the tab that says "Console".
You should see something that says "{ copyrightNotice: p }", and if you expand it, then you should be able to see all the properties associated with that variable. It's not particularly useful in this simple example, but I use this technique virtually every day as a web programmer.
Debugging using the Chrome debugger
Keep the dom example open in Chrome. Then press shift + ctrl + c to open the browser console. Navigate over to the "Sources" tab. This tab is the built in debugger provided by Google Chrome. In this debugger, you can add breakpoints to your code, and the browser will stop at the breakpoints and allow you to examine the state of your code. It's okay if this looks very confusing. We don't need to do much with it.
There are three things you need to be able to do. First, you need to be able to navigate to the sources tab. Then you need to find the line numbers, because you need to click them. This is much more difficult in a typical web page that has many files. Finally, you need to know how to continue running the page after it stops at your breakpoints.
After you've opened up the page, click line 12 to set a breakpoint there. In this case, that code has already been run, so reload the page to make it run again. When you do so, you should see something like the following.
Notice the area that I've circled in that screenshot. It's where you can see the state of your code. All of your variables should appear there and you should also see their values. Notice that copyrightNotice is undefined in this case. That's because Chrome pauses before running the code at the line where you placed your breakpoint. If you place a breakpoint on the next line, then press the play/pause button to resume execution, the value for copyrightNotice should be filled in.
The Chrome debugger is a great way to examine the state of your code.
Project: Client-side web form
This post contains the second assignment in my free web programming course.
Project: Client-side web form
Forms are how users enter data on the web. When you register for a website, you enter your username and email into a web form. When you buy an item from a web store, you send your payment information using a web form.
Generally speaking web forms have input components on them like text boxes and dropdowns and buttons. But this isn't always the case. Web forms can be cleverly hidden using JavaScript and CSS.
Typically a web form submits data to a server, which processes that data and responds to the user with something interesting. For instance, you might submit your username and password and get back an authentication token that allows you to use the private portion of a website.
Requirements
I want you to build a single web page using html and javascript only.
It should have three things on it.
- A page title in h1 tags.
- A label that reads "title:".
- An input text box.
- A submit button.
When the button is clicked, the title of the page should change to the title entered by the user without reloading the page.
Also, the user input must validated. The user should be prevented from using dirty words like "MacOS" and "iphone". Don't let them use either of those words in any capitalization. If those words occur in the input, then a red error message should be displayed underneath the text box that reads "Please refrain from using dirty words such as 'MacOS' and 'iphone'.".
Extra credit
For extra credit, make the form beautiful using CSS. Be creative, but try to make something that a typical web user might like.