

It’s much easier to use endless ratchet straps. These one-piece straps don’t have hooks.



Let’s say you’ve inherited a large website that uses some home-grown static site generator (SSG) and there’s no documentation. Your build and release infrastructure is fragile and also custom. Your git repo is massive with content from two decades, including lots of binary files. You want to migrate this massive piece of shit to a popular SSG like Eleventy and you want to use a reliable deployment system like GitHub + Netlify. Let’s say you can’t migrate all source files because there’s no easy way to do so between your custom SSG and Eleventy. If you’re willing to sacrifice most of your layouts and partials (includes) and just migrate everything all of the built static files to Eleventy with one partial for the header and one for the footer, then here’s one way to do it.
If you don’t have access to the SSG and the web servers, you can download the whole website from the internet using wget.
If your website is on WordPress, install the Simply Static plugin and export your site to a zip file (free).
Let’s say your HTML looks something like this.
<html>
<head>
...
</head>
<body>
<header class="header">
<div>Logo</div>
<ul>
<li>Link 1</li>
<li>Link 2</li>
</ul>
</header>
<section>
<p>Hello, World!</p>
</section>
<footer class="footer">
<p>Copyright 2024</p>
<div>blah blah blah</div>
</footer>
</body>
</html>
In this case, you can create separate files like this:
/includes/header.njk
<header class="header">
<div>Logo</div>
<ul>
<li>Link 1</li>
<li>Link 2</li>
</ul>
</header>
/includes/footer.njk
<footer class="footer">
<p>Copyright 2024</p>
<div>blah blah blah</div>
</footer>
For simplicity, let’s say that your HTML looks something like this:
<html>
<head>
...
</head>
<body>
<div class="header">
<div>Logo</div>
<ul>
<li>Link 1</li>
<li>Link 2</li>
</ul>
</div>
<section>
<p>Hello, World!</p>
</section>
<div class="footer">
<p>Copyright 2024</p>
<div>blah blah blah</div>
</div>
</body>
</html>
If your header and footer code blocks don’t use unique HTML tags like “header” and “footer”, then you may have a problem searching and replacing these code blocks. For example, in VS Code, if I try to select the header block beginning with <div class="header">
, I can’t do so due to the nested div tag.
Using the regex
<div class="header"(.|\n)*?</div>
notice how the selection ends prematurely at the closing nested div tag. In this situation, you can update your source code to replace the open and closing div tags with the standard <header> tag. You can do the same with the footer by using the <footer> tag. After updating the source code, you can rebuild your static HTML pages and then use a regex like
<header(.|\n)*?</header>
<footer(.|\n)*?</footer>
to search and replace the header and footer code blocks with a code reference that includes those code blocks using whatever template engine you want to use.
If you want to use the Nunjucks template engine, for example, then you can replace those code blocks with something like
{% include "header.njk" %}
{% include "footer.njk" %}
Rename all HTML files so their extensions are .njk instead of .html.
brew install rename
find . -name "*.html" -exec rename 's|\.html|\.njk|' {} +
Create a new folder and install an SSG. In this case, I’ll install Eleventy.
mkdir mysite
cd mysite
npm init -y
npm install --save-dev @11ty/eleventy
Move your website files to your new Eleventy project. To follow Eleventy’s default conventions, your folder structure should look something like this.
Note that we put the header and include partials in the “_includes” folder under the “src” folder. Therefore, our header and footer include references should be updated to look like this
<html>
<head>
<title>Home Page</title>
</head>
<body>
{% include "src/_includes/header.njk" %}
<section>
<p>Hello, World!</p>
</section>
{% include "src/_includes/footer.njk" %}
</body>
</html>
If you don’t create an Eleventy config file, then Eleventy will use all of its defaults and output built files to a “_site” folder and it will build the partials as well.
Since we don’t want to build the partials, let’s create an Eleventy config file.
In the project root, create a file called .eleventy.js with the following content.
module.exports = function(eleventyConfig) {
eleventyConfig.addPassthroughCopy("src", {
//debug: true,
filter: [
"404.html",
"**/*.css",
"**/*.js",
"**/*.json",
"!**/*.11ty.js",
"!**/*.11tydata.js",
]
});
// Copy img folder
eleventyConfig.addPassthroughCopy("src/img");
eleventyConfig.setServerPassthroughCopyBehavior("copy");
return {
dir: {
input: "src",
// ⚠️ These values are both relative to your input directory.
includes: "_includes",
layouts: "_layouts",
}
}
};
If you rerun Eleventy, you’ll see that the partials are not built and copied to the output folder.
If you want your page content to be wrapped in other content, you can create a layout. This is called template inheritance. Both Nunjucks and 11ty have their own template inheritance mechanism. With Nunjucks, you inherit a parent template using
{% extends "parent.njk" %}.
With 11ty, you inherit a parent template using front matter, e.g.
---
layout: parent.njk
---
Nunjucks supports template blocks natively, but it doesn’t support front matter. 11ty supports front matter, but it doesn’t support template blocks natively. Learn more about creating a layout using 11ty’s template inheritance mechanism.
If you have absolute links to the same site, you should replace them with relative ones, e.g.
https://www.mysite.com/products/ -> /products/
You can do this easily in VS Code’s search and replace feature.
If your website images are local, I would move them to AWS S3 and then use the S3 URL as a custom origin to pull the images into an image CDN like ImageKit or Cloudinary. If your site is in WordPress, the images will be in the wp-content/uploads folder. You can then do a global search and replace to reference the image CDN URL.
Recently, I needed to clone a website and make a few minor changes to it. I wanted to publish a slightly modified copy of the website. Luckily, it’s easy to do that using wget. Here’s how I did it.
I’m on Mac, so I installed wget using Homebrew using the command
brew install wget
I wanted to download this small website. I used this command:
wget -p -r https://events.govexec.com/qualys-cyber-risk-conference/
Note that wget will crawl a website to download pages and dependencies. If a page is not linked, whether directly or indirectly from the URL passed to the wget command (an orphan page, it will not get downloaded. One option is to make a list of all URLs (one per line) by getting them from the websites sitemap, assuming the sitemap is complete, and passing that list to the wget command, e.g.
wget -p -r --input-file=download-list.txt
If download stops, you can continue using the command
$ wget -p -r --continue --input-file=download-list.txt
Since I downloaded a bunch of HTML files, if I wanted to replace a common element on multiple pages, the easiest way was to do a search and replace. Using VisualStudio Code, you can easily find all HTML blocks within a particular tag using a multi-line regex. Here are some example regexes:
<footer(.|\n)*?</footer>
<script(.|\n)*?</script>
<a class="popup(.|\n)*?</a>
Note: these regexes only work if the tags don’t have any nested tags with the same name.
In some companies, some people have way too many meetings. Of course, some meetings are necessary, like when you need to discuss an issue. But some meetings are pretty much just status updates. For example, within a marketing department, you will have many teams, including public relations, events, web, design, content, campaigns, etc. Within each team, you’ll have a team lead and other people of varying ranks. What some companies or departments do is they have long meetings every month or so where everyone attends. Then, the team lead from each unit takes turns giving a status update. While this may seem like a good use of everyone’s time, it’s actually dumb as hell. Many, if not most, people will not care about what other teams are doing because the activities of other teams are simply irrelevant to them. They may try to pay attention, but because much of what is said doesn’t matter to them, they will likely forget what was said within a few days if not hours, resulting in a complete waste of many people’s time. Another problem is time management. If each speaker is given 5 minutes to talk, most likely they will talk for much longer and not everyone will be able to share their updates or the meeting will just last for much longer than it needs to. If your team is spread across multiple time zones, e.g., the US and India, then people will inconveniently have to attend these pointless meetings early in the morning or late in the evening. Interestingly, some people are in so many meetings that they don’t even have time to do any of the actual work that they’ve discussed in the meetings. Another problem with these types of periodic (weekly or monthly) status update meetings is people are forced to try to remember their activities or accomplishments, put them in a few Powerpoint slides, and then wait till the meeting happens, only for the activities to become old news because they happened too long ago.
For a live meeting to be effective, it should meet the following criteria:
For discussions that don’t need to be in real time (asynchronous discussions), then communicating via chat where only relevant people are involved is usually effective.
For status updates, they are actually more effective when they are written, e.g, via group chat, as long as they are concise and formatted well so that people can easily consume all of it or just the parts that are relevant to them. This also gives people time to think about a particular update and follow up with relevant people if needed. It also allows anyone, not just team leads, to post important updates as soon as they happen.
Following are some quotes from various sources on the topic of providing status updates. Many of these quotes are from companies that provide communication and collaboration tools. Regardless, they do have a point.
Status updates don’t belong in meetings. Status updates are ineffective team meetings, says Baker. “A round-robin of what people are working on can be handled over email or a collaboration tool.”
Slack (from Salesforce)
Status updates are the hallmark of a poorly run meeting. Let’s say every week, each person on your team goes around in a circle and provides a high-level update of what they are working on. Without fail, many of these items are ONLY relevant to a few people.
friday
Sitting through another status meeting where the project manager reads through a spreadsheet list or flips through slides on a presentation isn’t a productive use of anyone’s time. In fact, 56% of US workers get irritated by meetings that could’ve been an email. To make the best use of your team’s time, switch to asynchronous meetings where possible.
When updates or discussions aren’t relevant for the entire team, some people can end up disengaging. Inviting too many people who don’t need to be there can also cause meetings to become irrelevant. Set a high bar for whole-team meetings. Reserve meetings involving the entire team or a larger group for topics that truly require collective discussion and decision-making.
Switchboard
To be plain, many status meetings don’t need to happen. Instead, many status update meetings could be emails, memos, threads on collaboration platforms like Slack, or quick check-ins between colleagues on their own. Many status meetings happen by tradition: “We always do a Monday morning status meeting.” “Wednesday are for team updates.”
BetterMeetings
Almost 50 percent of respondents in a recent poll said they would rather go to the DMV or watch paint dry than sit in a status meeting.
Lucid meetings
One of the worst kinds of status meetings to attend is the type in which everyone goes around the room and states what they accomplished the prior week. This practice is a colossal waste of time. If this is the entire point of the meeting, then don’t call one. An effective PM can get these updates prior to a meeting, distribute them and then discuss issues at the meeting.
Amanet
Why Weekly Synch Meetings Are Boring and A Waste of Time [Rhythm Systems]
Sunrise | Sunset | |
---|---|---|
May 11, 2025 | 5:46 AM | 6:53 PM |
May 19, 2025 | 5:43 AM | 6:56 PM |
Start Time | End Time | Activity |
---|---|---|
4:21 PM | 5:49 PM | Fly from SF (SFO) to Los Angeles (LAX) |
5:49 PM | 9:00 PM | 3-hour layover at LAX |
9:00 PM | Fly from LAX to Jeddah, KSA |
Start Time | End Time | Activity |
---|---|---|
10:20 PM | Land in Jeddah, KSA | |
11:00 PM | 11:30 PM | Driver takes us from airport to Luli’s house |
Start Time | End Time | Activity |
---|---|---|
7:00 AM | 8:00 AM | Breakfast at … (Shakshouka, Roz Bi-Laban) |
8:30 AM | 9:00 AM | Uber from Luli’s house to Jeddah airport train station |
10:00 AM | 11:45 AM | Bullet train from Jeddah airport to Medina ($62) |
12:00 PM | 12:30 PM | Uber from train station to the Prophet’s mosque |
12:30 PM | 1:00 PM | Lunch at Al Baik (in front of mosque) |
1:00 PM | 4:30 PM | Visit the Prophet’s mosque |
4:30 PM | 5:30 PM | Dinner at … |
5:30 PM | 6:00 PM | Uber to train station |
7:00 PM | 8:48 PM | Bullet train from Medina to Jeddah ($62) |
9:00 PM | 9:30 PM | Uber to Nawal’s house |
Start Time | End Time | Activity |
---|---|---|
Breakfast at … (Shakshouka, Roz Bi-Laban) | ||
9:00 AM | Hired driver (Ali) picks us up at Nawal’s house. Bring all bags. | |
9:00 AM | 10:00 AM | Drive from Nawal’s house in Jeddah to Farida’s house in Mecca |
10:00 AM | 10:30 AM | Rest / chat with Farida |
10:30 AM | 11:00 AM | Leave small bags at Farida’s house. Drive to Miqat of Tan’im (Masjid-e-Aisha) 7.5 KM away from Masjidil Haram. |
11:00 AM | 11:30 AM | Pray sunnah with intention to do umrah |
11:30 AM | 12:00 PM | Drive to Masjid Al-Haram |
12:00 PM | 3:00 PM | Do umrah (tawaf and sa’i). Driver drives back to Jeddah and drops off our luggage at Riyadh’s house. |
3:00 PM | 4:00 PM | Lunch at Al Shorfa (next to mosque) |
4:00 PM | 4:10 PM | Uber to Farida’s house |
8:00 PM | 9:00 PM | Bullet train from Mecca to Jeddah airport ($34) |
9:00 PM | 9:30 PM | Uber to Riyadh’s house |
10 PM | 11 PM | Buy Baklava at Baklavaci بكلافجي |
Start Time | End Time | Activity |
---|---|---|
8:00 AM | 9:00 AM | Breakfast at Maison De Zaid دار زيد |
12:00 PM | 1:00 PM | Lunch at Ulfat Mutabbaq |
3:00 PM | 4:00 PM | Drive to Arabian Treks Campground |
4:00 PM | 9:00 PM | Desert Safari (includes dinner) |
9:00 PM | 10:00 PM | Drive back to Jeddah |
Start Time | End Time | Activity |
---|---|---|
8:00 AM | 9:00 AM | Breakfast at … (Shakshouka, Roz Bi-Laban) |
12:00 PM | 1:00 PM | Go to Marina |
1:00 PM | 7:00 PM | Private boat ride to Bayada Island in the Red Sea |
9:00 PM | 10:00 PM | Dinner at Al Nakheel Lebanese restaurant |
Start Time | End Time | Activity |
---|---|---|
6:00 AM | 9:00 AM | |
2:00 PM | 3:00 PM | Lunch at Saraya Latif Turkish restaurant |
5:30 PM | 6:00 PM | Uber to Al-Balad |
6:00 PM | 6:30 PM | Coffee at Roshan Cafe |
6:30 PM | 8:30 PM | Jeddah Old Town Walking Tour (Al-Balad) The place comes alive at night, so best to go right before sunset, which is at 7 PM. |
8:30 PM | 9:00 PM | Dinner at Al-Basali Seafood Restaurant in Al-Balad |
9:00 PM | 10:00 PM | Walk the Jeddah corniche at night / Jeddah Yacht Club |
Start Time | End Time | Activity |
---|---|---|
9:00 AM | 10:00 AM | Breakfast at Twenty Four Restaurant |
12:00 PM | 1:00 PM | Buy Baklava to bring home |
1:00 PM | 2:00 PM | Lunch at … |
3:30 PM | 8:00 PM | Off-Road Dirt Bike Tour in Bahrah |
9:00 PM | 10:00 PM | Dinner at Al-Romansiah Saudi Restaurant |
Start Time | End Time | Activity |
---|---|---|
Breakfast at … (Shakshouka, Roz Bi-Laban) | ||
8:00 AM | 11:00 AM | E-bike along the corniche |
12:00 PM | 1:30 PM | Lunch at Asli Basha Turkish restaurant |
4:00 PM | 5:00 PM | Red Sea Rush Boat Ride |
5:00 PM | 5:30 PM | Jet skiing in the Red Sea |
8:00 PM | 9:30 PM | Dinner at Khayal Turkish restaurant |
Start Time | End Time | Activity |
---|---|---|
6:00 AM | Go to airport | |
9:50 AM | 4:05 PM | Fly from Jeddah to Los Angeles |
4:05 PM | 6:55 PM | 3-hour layover in Los Angeles |
6:55 PM | 8:15 PM | Fly from Los Angeles to San Francsisco |
This neighborhood is from the 7th century.
Google Map
Hours: 1 PM – 11:30 PM
Have Saudi coffee on the rooftop of Roshan Cafe
Take a picture like this
The following houses are a little bit out of the way.
Bikes
Morning tour: 6 AM – 9 AM
Afternoon tour: 3:30 PM till sunset (7 PM)
Cost: 500 SAR ($133)
Includes bike, helmet, body protection.
Does not include transportation to and from Bahrah.
Contact
Terminal 1
With so many video codecs and containers, it’s easy to get confused. Here’s a simple explanation.
Codec stands for coder-decoder. Video codecs are algorithms for encoding and decoding video data.
For simplicity, you can think of a video codec as the video format.
Examples of video codecs are H.261, H.263, VC-1, MPEG-1, MPEG-2, MPEG-4, AVS1, AVS2, AVS3, VP8, VP9, AV1, AVC/H.264, HEVC/H.265, VVC/H.266, EVC, LCEVC
Currently, the most popular codec is AVC/H.264.
With respect to video, a container is a data storage. It can include compressed video and audio sequences, subtitles, service information and metadata. It is a package or bundle.
For simplicity, you can think of a media container as the file format.
Examples of media containers are MPEG-1 System Stream, MPEG-2 Program Stream, MPEG-2 Transport Stream, MP4, MOV, MKV, WebM, AVI, FLV, IVF, MXF, HEIC
Currently, the most popular container is MP4.
There are many ways and tools you can enhance a photo. If you’re a professional photographer, then you’ll likely have advanced methods, but for the average person, you’ll probably just want some quick and easy solutions. Like most average people, my photos are mostly taken from my phone (currently, Google Pixel 8). However, when vacationing, I also take a lot of video using my Insta360 X3 camera, and I’ll occasionally want to take snapshots of a video frame to add to my photo collection. With this in mind, here’s my current (simple) workflow for upscaling and enhancing photos.
First, upload your photo to Google Photos. Then, use one of the presets to enhance the photo. Here’s an example photo without any enhancements applied.
The average person might that the photo looks fine, but it can significantly be improved. Here’s how the photo looks when you click on each of the suggested improvement options.
Note that Color Pop tried to isolate the subject and convert everything else to grayscale. It’s not perfect because the subject’s right arm is partially gray. To fix this, you could select the subject in Photoshop either manually or automatically, invert the selection, and convert the selection to grayscale.
Now, let’s say that you have a heavily underexposed (dark) photo. (Learn more about under and overexposed photos).
If you click the “Enhance” or “Dynamic” options, you’ll get this.
For comparison, here are the photo’s input levels in Photoshop.
If I were to manually correct the exposure in Photoshop, this is what I’d get.
The photo is significantly improved, but it doesn’t look exactly like it does using the Google Photos presets.
If the Google Photos presets don’t look good enough, you can make many adjustments in the Settings tab. In the example below, I started by choosing the “Dynamic” preset, and then in the Settings tab, I increased the brightness.
So, for the average person, using Google Photos to improve photos is easy and usually adequate.
Topaz Photo AI can do many things to a photo, including
You can also just run autopilot and let Topaz choose settings for you.
For me, I mainly use Topaz to enlarge (upscale) photos, remove noise, which can result from adjusting the levels of a heavily underexposed photo, and to sharpen photos. These improvements are particularly useful when I take a snapshot of a 1920×1080 video frame. For example, here’s a frame from a video.
I want to zoom in on the subject, crop it, enlarge it, and enhance it. Here’s the zoomed-in part cropped. The dimensions are 1048 x 589 px.
Now, I’ll drag it into Topaz and run autopilot to upscale and enhance the photo. It will take a minute to process. Here’s how the photo looks enlarged by 34% before enhancing it with Topaz.
Here’s how it looks with Topaz enhancements applied.
There is a difference, but it will be more obvious when you zoom in. Below is a comparison zoomed in at 67% before and after using Topaz.
At this point, you can copy the upscaled and sharpened photo from Topaz and paste it into Google Photos to enhance it.
Topaz Photo AI isn’t perfect, but depending on the original photo, it can often product amazing results.
Whether you’re planning to fast before a medical operation or otherwise, or don’t plan to have access to much food for a long period, I’ve found the following food incredibly effective at staving off hunger. Ramadan fasters, take note! The following foods have a low glycemic index and are digested slowly.
Warning: consuming some of these foods may cause bloating and flatulence, but if you consume it every day, those symptoms will go away within a week.
Available at Costco, this granola is high in fiber, healthy and delicious. The downside is it has 8 grams of added sugar. Costco should make a version replacing sugar with the all-natural, zero-calorie Monk fruit. I prefer to consume a bowl of this with unsweetened almond or soy milk and optionally some berries. Learn more.
Also available at Costco, these prepackaged overnight oats are convenient and healthy. There’s no added sugar, but they’re still sweet. Learn more.
Greek yogurt is very thick compared with regular yogurt and typically higher in protein, too. This particular one is sweetened with all-natural stevia extract, which is better than cane sugar and artificial sweeteners like sucralose. I consider this to be optional.