Backend Software Engineer#

👋 I’m Cedric Chee. I’ve been a software engineer, writer, and entrepreneur.

I code and write about it sometimes. I create system softwares and apps in Go/JS.

I do product engineering and web development at startups/consulting. I enjoy backend development.

I’m currenly focusing on Large Language Models (LLMs). I tinker with LLMs and AI systems at night.

Read more on the about page →

Recent Posts

My Year In Review report since 2017

All my “Year in Review” RescueTime reports from 2017 to 2020.

2020#

2020 report

2020 full report



2019#

2019 report

2019 full report



2018#

2018 report

2018 full report


I have been using RescueTime since 2016 and a “premium” customer from 2017. However, 2017 report is missing as I couldn’t generate it from RescueTime website now.

The Why#

These reports are useful data for my monthly self retrospective session. By sharing this data publicly, I hope you can learn and get a sense about my productivity for my primary role as a software engineer.


Oh, I hope it’s not too late to wish everyone, a happy and productive 2021!

That’s all I have for today. Taa…

To Rust or Not

A quick opinion.

Rust when:

  • Correctness is important – it provides more tools to help you write correct code and express invariants in a machine-checkable way.
  • Performance is important – either for single threaded programs or for those programs that benefit from concurrency or parallelism. Rust is a good option for some programs where there’s a clear hot loop. Rust is a perfect fit for workloads that have relatively flat CPU profiles where the performance bottlenecks are making memory allocations or similar.
  • Backward compatibility is important – the commitment to backward compatibility from the Rust authors means that you don’t get regular breakage simply from updating to a newer version of the language.

That doesn’t mean you’d always choose Rust.

Rust is not a good fit when:

  • It’s too much to give away the benefit from not requiring a compile step and making use of the ubiquity of a interpreter.
  • It’s difficult to justify using Rust for a typical web backend that’s mostly composing together various well-tested libraries to provide an API on top of a database.

Rust can makes hard things easy and easy things hard.


If you’re here and are interested in learning Rust, check out my Awesome Rust gist. I created this while I was learning Rust language in 2019. If you’re in a hurry or need a refresher, there’s a good post for that, “Learn Rust in half hour”.

First Taste of Generics in Go

This friendly, down-to-earth tutorial explains what generic functions and types are, why we need them, how they work in Go, and where we can use them.

Generic functions in Go#

func PrintAnything(type T)(thing T) {
}

func main() {
    PrintAnything(int)(99)
}

GPT-3 Application Ideas

Part of my side project, I’ve been researching and curating a list of NLP resources focused on BERT, GPT, Transformer networks, and more for over two years.

GPT-3 (Generative Pretrained Transformer) came from the Transformer family.

This year OpenAI is back with new language model GPT-3 and is currently making wave around the Internet. It’s interesting to see what creative app ideas are possible using a bigger GPT-3 model. Below are a few random selection of such apps:

Code Generator#

Generative Text#

More about the new kinds of tools and applications that people are building on GPT-3 API.

Effective Software Testing Practices

Writing down what you learn is key to your retention. Today I learned a bit on the wisdom of software testing and took some notes that I thought interesting enough to share.

I will not get into testing techniques this time. I will try to get more specific next time.

Find Important Bugs#

Test:

  • core functions before supporting functions. Core functions are critical and the top N things that the product does. It’s the functions that make the product what it is.
  • capability before reliability. Test whether each function can work at all before going deep into the examination of how any one function performs under many different conditions.
  • high-impact problems. Test the parts of the product that would do a lot of damage in case of failure.
  • common situations before niche situations.
  • the most wanted areas before areas not requested. This mean, any areas and for any problems that are of special interest to someone else.
  • things that are changed before things that are the same. Fixes and updates mean fresh risk.
  • common threats before rare threats. Test with the most likely stress and error situations.

Mindset#

  • Like to dispel the illusion that things work.
  • Critical thinking — critical examination of belief.
  • If you want to be a good tester, learn to think like one, not look like one.
  • Anticipate risks that the programmer missed — The more you learn about a product, and the more ways in which you know it, the better you will be able to test it.
  • Learn about systems thinking.
  • Intuition is often strongly biased.
  • Be an explorer.
  • What you think “it works” means might not match someone else’s definition.
  • Don’t confuse the test with the testing.
  • Manage bias.
  • Convince yourself that you are easy to fool.
  • When you know a product well, you make more assumptions about it, and you check those assumptions less often.
  • Don’t restrict yourself to being a steward of received wisdom; be the author of your own wisdom.

Ideas#

Use heuristics to generate ideas for tests. Examples:

  • Test at the boundaries.
  • Test every error message.
  • Test configurations that are different from the programmer’s.
  • Run tests that are annoying to set up.
  • Avoid redundant tests.

When Might Microservices Be a Bad Idea?

When might microservices be a bad idea?#

Well, it’s mid of 2020. If you are in the software development field, you should somehow bump into posts and/or discussions that say microservices is an anti-pattern — more services, more pain. As confusing as it is, today, I steal some time from my usual day to try to dissect this topic.

So, I’ve watch this GOTO 2019 talk by Sam Newman on monolith decomposition patterns. It’s one of the best talks on the topic I’ve seen.

Monolith Decomposition Patterns#

  • Isolate the data
  • Release train
  • Horror, pain and suffering
    • Microservices are not the goal — you don’t win by doing microservices.
    • It’s so silly when people start comparing how many microservices you got.
  • Strangler fig applications (“wraps around” existing system)
    • Incremental migration of functionality from one system to another.
  • Branch by abstraction
    • Create an abstraction for the functionality to be replaced.
    • You can also learn more by reading “Working Effectively with Legacy Code” book by Michael Feathers.
  • Parallel run
    • Rather than calling either the old or the new implementation, instead we call both.
  • Decompose the database
    • You can also learn more by reading “Refactoring Databases” book by Scott Ambler and Pramod Sadalage.
  • Partitions
    • Split table

I’ve also read a wide-range of posts on this topic to get a better understanding.

The following key takeaways are taken from InfoQ Podcast:

  • Fundamentally, microservices are distributed systems. Distributed systems have baggage (complexity) that comes along with them. The best way to deal with this complexity is not to address it. Try to solve the problem in other ways before choosing to take an organization to microservices.

  • A common issue that large enterprises run into that might be a strong indicator for implementing microservices occurs when lots of developers are working on a given problem and they’re getting in each other’s way.

  • A useful structure to follow with microservices is to make sure each service is owned by exactly one team. One team can own more than one service but having clear ownership of who owns a service helps in some of the operational challenges with microservices.

  • A release train should be a stop in the journey towards continuous delivery. It’s not the destination. If you find that you can only release in a release train, you are likely building a distributed monolith.

  • There are challenges of operating microservices when the end customer has to operate and manage it. These challenges are part of why we’re seeing projects move from microservices to process monoliths.

I think, these takeaways can act as a good summary for the videos, talks, and articles I’ve seen.

References#

  1. Monolith To Microservices book by Sam Newman.
  2. How to break a Monolith into Microservices article by ThoughtWorks.

Summary of "Clean Code" by Robert C. Martin

A summary of the main ideas from the “Clean Code: A Handbook of Agile Software Craftsmanship” book by Robert C. Martin (aka. Uncle Bob).

Code is clean if it can be understood easily – by everyone on the team. Clean code can be read and enhanced by a developer other than its original author. With understandability comes readability, changeability, extensibility and maintainability.


General rules#

  1. Follow standard conventions.
  2. Keep it simple stupid. Simpler is always better. Reduce complexity as much as possible.
  3. Boy scout rule. Leave the campground cleaner than you found it.
  4. Always find root cause. Always look for the root cause of a problem.
  5. Follow the Principle of Least Surprise.
  6. Don’t repeat yourself (DRY).
  7. Do not override safeties.

Design rules#

  1. Keep configurable data (e.g.: constants) at high levels. They should be easy to change.
  2. Prefer polymorphism to if/else or switch/case.
  3. Separate multi-threading code.
  4. Prevent over-configurability.
  5. Use dependency injection.
  6. Follow Law of Demeter. A class should know only its direct dependencies.

Understandability tips#

  1. Be consistent. If you do something a certain way, do all similar things in the same way.
  2. Use explanatory variables.
  3. Encapsulate boundary conditions. Boundary conditions are hard to keep track of. Put the processing for them in one place.
  4. Prefer dedicated value objects to primitive type.
  5. Avoid logical dependency. Don’t write methods which works correctly depending on something else in the same class.
  6. Avoid negative conditionals.

Names rules#

  1. Choose descriptive and unambiguous names.
  2. Make meaningful distinction.
  3. Use pronounceable names.
  4. Use searchable names.
  5. Replace magic numbers with named constants.
  6. Avoid encodings. Don’t append prefixes or type information.

Functions rules#

  1. Small.
  2. Do one thing and they should do it well.
  3. Use descriptive names.
  4. Prefer fewer arguments. No more than 3 if possible.
  5. Have no side effects.
  6. Don’t use flag arguments. Split method into several independent methods that can be called from the client without the flag.

Comments rules#

  1. Always try to explain yourself in code. If it’s not possible, take your time to write a good comment.
  2. Don’t be redundant (e.g.: i++; // increment i).
  3. Don’t add obvious noise.
  4. Don’t use closing brace comments (e.g.: } // end of function).
  5. Don’t comment out code. Just remove.
  6. Use as explanation of intent.
  7. Use as clarification of code.
  8. Use as warning of consequences.

Source code structure#

  1. Separate concepts vertically.
  2. Related code should appear vertically dense.
  3. Declare variables close to their usage.
  4. Dependent functions should be close.
  5. Similar functions should be close.
  6. Place functions in the downward direction.
  7. Keep lines short.
  8. Don’t use horizontal alignment.
  9. Use white space to associate related things and disassociate weakly related.
  10. Don’t break indentation.

Objects and data structures#

  1. Hide internal structure.
  2. Prefer data structures.
  3. Avoid hybrids structures (half object and half data).
  4. Should be small.
  5. Do one thing.
  6. Small number of instance variables. If your class have too many instance variable, then it is probably doing more than one thing.
  7. Base class should know nothing about their derivatives.
  8. Better to have many functions than to pass some code into a function to select a behavior.
  9. Prefer non-static methods to static methods.

Tests#

  1. One assert per test.
  2. Fast.
  3. Independent.
  4. Repeatable.
  5. Self-validating.
  6. Timely.
  7. Readable.
  8. Easy to run.
  9. Use a coverage tool.

Code smells#

  1. Rigidity. The software is difficult to change. A small change causes a cascade of subsequent changes.
  2. Fragility. The software breaks in many places due to a single change.
  3. Immobility. You cannot reuse parts of the code in other projects because of involved risks and high effort.
  4. Needless Complexity.
  5. Needless Repetition.
  6. Opacity. The code is hard to understand.

Error handling#

  1. Don’t mix error handling and code.
  2. Use Exceptions instead of returning error codes.
  3. Don’t return null, don’t pass null either.
  4. Throw exceptions with context.

Adapted from wojteklu/clean_code.md.

All credit goes to the rightful owner.

Different Ways to Create Objects in JavaScript

JavaScript has a number of predefined objects. In addition, you can create your own objects.

Using object initializers#

It is sometimes referred to as creating objects with literal syntax. It’s one of easiest way to create an object by simply define the property and values inside curly braces.

let product = {
  mfgCountry: 'Indonesia'
};
product.name = 'Surgical Mask';
product.category = 'medical';

Using Object class#

let product = new Object(); // store an empty Object object in product.
product.name = 'Surgical Mask';
product.category = 'medical';
product.mfgCountry = 'Indonesia';

Using Object’s create method#

// Create a new object using an existing object as the prototype.
let product = Object.create({
  name: this.name,
  category: this.category,
  mfgCountry: 'Indonesia'
}, {
  name: { value: 'Surgical Mask' },
  category: { value: 'medical' },
});

Using Object.prototype.constructor function#

function Product(name, category) {
  this.name = name;
  this.category = category;
  this.mfgCountry = 'Indonesia';
}
let product = new Product('Surgical Mask', 'medical');
console.log('product.constructor is ' + product.constructor);

Using ES6 Class syntax#

JavaScript classes are primarily syntactical sugar over JavaScript’s existing prototype-based inheritance.

class Product {
  constructor(name, category) {
    this.name = name;
    this.category = category;
    this.mfgCountry = 'Indonesia'
  }
}
let product = new Product('Surgical Mask', 'medical');

Using Immediately Invoked Function Expression (IIFE)#

IIFE is a design pattern which is also known as a self-executing anonymous function.

let product = new(function (name, category) {
  this.name = name;
  this.category = category;
  this.mfgCountry = 'Indonesia';
})('Surgical Mask', 'medical');

Using Function constructor with prototype#

(Note: this is NOT one of the way to create a JavaScript object with the same behavior as the rest of the examples in this post because it set the property on the prototype and not the object it self.)

function Product() {
  this.mfgCountry = 'Indonesia';
}
Product.prototype.name = 'Surgical Mask';
Product.prototype.category = 'medical';
let product = new Product();

I deliberately show this example because this is where a lot of confusion usually begins with JavaScript prototypes.

Intro to Deno

Deno is a secure runtime for JavaScript and TypeScript.

Deno is a brand new general-purpose JavaScript programming environment, similar to Node.js. It is what Node.js should have been according to Ryan Dahl who created both tools.

Deno just hit v1.0.0-rc1. David Else has written a nice article about Deno 1.0: what you need to know.

This guide should give you something you need to get started.

Getting Started#

  • Running a simple Hello World script
  • Writing our own script
  • Using Deno with TypeScript

Installation#

Full guide for installing Deno for other platforms.

Using shell (macOS and Linux):

$ curl -fsSL https://deno.land/x/install/install.sh | sh
######################################################################## 100.0%##O=#  #                                                                      
Archive:  /home/cedric/.deno/bin/deno.zip
  inflating: deno                    
Deno was installed successfully to /home/cedric/.deno/bin/deno
Manually add the directory to your $HOME/.bash_profile (or similar)
  export DENO_INSTALL="/home/cedric/.deno"
  export PATH="$DENO_INSTALL/bin:$PATH"
Run '/home/cedric/.deno/bin/deno --help' to get started

Example - Running a simple “Hello World” script#

Try running a simple program:

$ deno run https://deno.land/std/examples/welcome.ts
Download https://deno.land/std/examples/welcome.ts
Compile https://deno.land/std/examples/welcome.ts
Welcome to Deno 🦕

Deno tries to be web compatible and use modern features whereever possible.

Writing our own script#

Next, we will go through some simple examples that can teach you about the fundamentals of Deno.

Web server#

$ vim web_server.ts

Copy and paste the following code into your text editor.

import { serve } from "https://deno.land/std@v0.42.0/http/server.ts";
const s = serve({ port: 8000 });
console.log("http://localhost:8000/");
for await (const req of s) {
  req.respond({ body: "Hello world\n" });
}

Try the program:

$ deno run web_server.ts
Compile file:///home/cedric/my_files/deno-test/web_server.ts
Download https://deno.land/std@v0.42.0/http/server.ts
Download https://deno.land/std@v0.42.0/encoding/utf8.ts
Download https://deno.land/std@v0.42.0/io/bufio.ts
Download https://deno.land/std@v0.42.0/testing/asserts.ts
Download https://deno.land/std@v0.42.0/util/async.ts
Download https://deno.land/std@v0.42.0/http/io.ts
Download https://deno.land/std@v0.42.0/io/util.ts
Download https://deno.land/std@v0.42.0/path/mod.ts
Download https://deno.land/std@v0.42.0/path/win32.ts
Download https://deno.land/std@v0.42.0/path/posix.ts
Download https://deno.land/std@v0.42.0/path/constants.ts
Download https://deno.land/std@v0.42.0/path/common.ts
Download https://deno.land/std@v0.42.0/path/constants.ts
Download https://deno.land/std@v0.42.0/path/interface.ts
Download https://deno.land/std@v0.42.0/path/glob.ts
Download https://deno.land/std@v0.42.0/path/globrex.ts
Download https://deno.land/std@v0.42.0/path/utils.ts
Download https://deno.land/std@v0.42.0/fmt/colors.ts
Download https://deno.land/std@v0.42.0/testing/diff.ts
Download https://deno.land/std@v0.42.0/textproto/mod.ts
Download https://deno.land/std@v0.42.0/http/http_status.ts
Download https://deno.land/std@v0.42.0/bytes/mod.ts
error: Uncaught PermissionDenied: network access to "127.0.0.1:8000", run again with the --allow-net flag
    at unwrapResponse ($deno$/ops/dispatch_json.ts:43:11)
    at Object.sendSync ($deno$/ops/dispatch_json.ts:72:10)
    at Object.listen ($deno$/ops/net.ts:51:10)
    at listen ($deno$/net.ts:152:22)
    at serve (server.ts:261:20)
    at web_server.ts:2:11

You will see that this program returns an error regarding network access because Deno is a runtime that is secure by default.

$ deno run --allow-net web_server.ts
http://localhost:8000/

$ curl http://localhost:8000
Hello world

Making an HTTP request#

A small program that fetches a file from a web server and prints the content to the terminal.

// curl.ts

const url = Deno.args[0];
const res = await fetch(url);

const body = new Uint8Array(await res.arrayBuffer());
await Deno.stdout.write(body);

Just like in the browser you can use the web standard fetch API to make HTTP calls.

This program parse the response body as an ArrayBuffer, await the response, convert it into a Uint8Array and store it in the variable body.

$ deno run --allow-net curl.ts https://example.com
Compile file:///home/cedric/my_files/go-dist-services/curl.ts
<!doctype html>
<html>
<head>
    <title>Example Domain</title>

    <meta charset="utf-8" />

... truncated ...
... truncated ...

Reading a file#

Deno also provides APIs which do not come from the web. You can find documentation for these APIs on doc.deno.land.

Filesystem APIs for example do not have a web standard form, so Deno provides its own API.

// cat.ts

for (let i = 0; i < Deno.args.length; i++) {
  let filename = Deno.args[i];
  let file = await Deno.open(filename);
  await Deno.copy(file, Deno.stdout);
  file.close();
}

Try running the program:

$ deno run --allow-read cat.ts hello.txt
Hello from Deno.

This illustrates a general design goal for I/O streams in Deno.

A simple TCP server#

An example of a simple server which accepts connections on port 8080, and returns to the client anything it sends.

// echo_server.ts

const listener = Deno.listen({ port: 8080 });
console.log("listening on 0.0.0.0:8080");
for await (const conn of listener) {
  Deno.copy(conn, conn);
}

Try:

$ deno run --allow-net echo_server.ts
listening on 0.0.0.0:8080

Try sending data to it with netcat:

$ nc localhost 8080
hello deno
hello deno

Like the cat.ts example, the copy() function here also does not make unnecessary memory copies. It receives a packet from the kernel and sends back, without further complexity.

Using TypeScript#

Please read the docs.

Learn more#

Dynamic Websites - "Everything old is new again"

I’ll start with a little bit of background.

So, I’ve developed several Single Page Applications (SPAs) using JavaScript over many years. These are a few picks of “large” JavaScript apps in production:

  • Pseudo 3D mobile racing game - build with Phaser framework
  • Hybrid mobile app - build with Apache Cordova (was PhoneGap) and jQuery
  • Integrated DICOM medical images viewer with accelerated anomaly detection backed by deep learning - build with React and Electron

I’ve also developed MVC web applications using RoR or Node.js. The view layer (UI) is server rendered.

Being on both sides, one of the pain point of developing SPAs using JavaScript library like React or Vue.js is, it is difficult to have good SEO if you are using client-side rendering (CSR) in React for your public facing website (like marketing site, blog, documentation site, content-heavy site like news and media). Yes, I am aware that Googlebot (web crawler) processes JavaScript.

There are a number of different ways to build a website. To solve this pain point (along with other problems with CSR), we use different techniques like Server-side rendering (SSR), prerendering, and so on.

To get the best from both worlds, I pick Next.js for some problems. Next.js is the React framework. Next.js render the HTML for a page on the server in response to navigation. Next.js is in the category of SSR with rehydration. Rehydration is “booting up” JavaScript views on the client such that they reuse the server-rendered HTML’s DOM tree and data.

It’s hard to get React SSR to work properly. This is where we see different React frameworks come into play.

In the same category of frameworks like Next.js is Remix (Cool site! And one more thing, it’s designed for Web Accessibility).

Finally, a killer React framework from the creators of React Router

Remix is a new React framework that provides APIs and conventions for server rendering, data loading, routing and more.

Check out the first preview of Remix. Don’t miss the video walkthrough in that post. You get to see:

… the best way to build dynamic React websites. You’ll get a preview on routing, layouts, data loading, meta tags, data caching, and scroll restoration.

In contrast to Next.js, IMO, if there’s one thing I felt clunky in Next.js and that is its router. It’s interesting to see how Remix approach this, how will this turns out eventually.

Alright, that’s all I have for you in this post. I hope you enjoy it. You can follow me for more such interesting stories on Twitter.












Hey, one more thing before you go, have you seen RedwoodJS or Blitz?

A Career and Code Retreat Retrospective — 16 years working in tech

This is part 1 of a series of upcoming posts on my career and code retreat retrospective — what has been great, what has been horrible. Please pardon my English as I am not a native English speaker.

What is Educational/Code Retreat#

I was excited to first learned about the idea of educational retreat from Julia Evans’ blog in 2015. She attended Hacker School. In the words of the Recurse Center (formerly known as Hacker School) website:

The Recurse Center is a free, self-directed, educational retreat for people who want to get better at programming, whether they’ve been coding for three decades or three months.

It’s as a retreat for programmers who want to get back into learning. You take sabbatical from work, away from deadlines and the stresses of everyday life. It’s sort of like going back to college, but you get to decide what you want to learn, and homework is never boring because you get to choose the fun stuff that you want to work on.

My Experience#

I spent 14 months in educational retreat from 2019 to Feb 2020 — 7 months as an Entrepreneur-In-Residence (EIR) at Antler and the rest of the time in code retreat ran by a startup in Asia. I’ve been meaning to write about the experience itself, and what happened after for a while now, so here goes!

The Journey#

When I moved to Singapore in 2006, my main objective is to start my own tech startup business and chase my dream. I work hard and grab every little opportunities offered to me that allow me to learn and grow my skills and career while working for companies. I am always hungry to learn. Learning never stops. I still remember vividly my first ever experience attending Barcamp 4 conference in 2009.

Career as a Full Stack Software Engineer#

I am a full stack software engineer all along (funnily, even before full stack title being a thing now). I mostly work on web developments in the early days of my career. I learned iOS (and Objective-C)1 and Android (Java) mobile development not long after the first iPhone and AppStore launched together with a group of people who started CocoaHeads meetup, buUuk and some worked at Apple now. I’ve blogged about the iPhone 3G launch and my first foray into Android development in 2010 after attended the Developer Lab by Google in my abandon blogs.

Front-end Development#

In my full stack web roles, I spent most of my time on front-end development, writing vanilla JavaScript, CSS, jQuery, Backbone, and a bit of MooTools. I picked up React.js a year after it was announced as open source project in 2013. I switched to React Native in 2015 (video) after working in PhoneGap. Since then, I have delivered over 5 major React projects to production, be it for my consulting customers or my own startups.

Backend Development#

Most of my experience in backend works are done in statically typed and compiled languages like C, C# or TypeScript. I have a short love with Ruby before Node.js came out. I have only done some toy projects with Ruby on Rails and that was long time ago. I have deep experience in Node.js together with a minimalistic MVC framework, Express.

I don’t have many achievements in backend development that I’m proud of. I think my favorite contributions to backend systems were my work optimizing SQL database performance for several large legacy websites, created my first in-house web framework from scratch, RESTful API design and implementation for small to medium-sized apps, and sysadmin lead for greenfield infra projects on AWS.

As you can see, I lack the experience and skills in working in high traffic and large apps where majestic monolith/macroservices (opposite of micro/services oriented architecture) runs in a distributed systems environment and learning to build secure, reliable, and maintainable systems are important skills.

More than Just Programming#

If you ask me to describe “what is software engineering in three words?”, my response is “more than programming”.

Throughout my time as a software engineer and consultant, I have lead technically several major projects and supported customers for both business and technical needs. My team are structured to work directly and closely with the customers in every project. This will minimize the cost of error due to communication gap. We wear many hats. We go beyond our own roles sometimes.

Mid-Career Switch#

Around 2013, I noticed my career growth stagnant even though I still learn every day on the job, but not much. So, I started making a change to the situation. At this point, I have learned enough the basics of building startup and high-growth business after getting to know entrepreneur27 (e27 now) and TechCrunch.

First Taste of Deep Learning#

In 2015, I was confronted with a problem that is not feasible to solve using existing way of manually hand-written programming. The problem is simple, such that, “set the web font color automatically while maintaining good contrast with its background color”.

As a curious learner and knowing the chance to step-up my learning several times, I spent a week exploring and researching for solution and eventually ended up discovering neural networks. As a self-taught Computer Science, I have no idea what is neural networks. But that didn’t stop me from learning.

Then, I stumbled upon Stanford CS231n MOOC taught by Andrej Karpathy and started learning deep learning, convnet, and Computer Vision. I was still struggling to wrap my mind around all the new neural nets theory and concepts and Maths around backpropagation/gradient descent calculations. So, I persisted and studied Coursera Machine Learning course taught by Prof. Andrew Ng and got certified.

The Growth Stage#

I was doing all that while having a full-time job. But, at the end of 2017, I left my 6 years job and pursue my dream. It’s my first sabbatical. I build my first startup using what I learned so far.

While working on my first startup in the medical domain, from 2018 onwards, I started my fellowship at fast.ai. Unlike Stanford’s courses, fast.ai teaching style is geared towards practical examples first, theory later and top-down learning (I get to play baseball before learning about the sport science). fast.ai courses complement well with the more formal courses from Stanford and Coursera. During this period, I learned immensely up to the point I am capable of moving beyond deep learning research and experimentation and taking real-world projects to production in my startup. Unfortunately, my startup failed to gain traction at the end of 2018 and demised.

Fast.ai International Fellowship#

I was accepted into Fast.ai International Fellowship 2018 program. As a fellow, I worked on a variety of data science and deep learning projects such as:

  • Awesome BERT NLP repo - I have been tracking and researching Transformers networks and transfer learning in NLP for more than a year. Along the way, I share and update the repo with stuffs I find awesome.
  • I wrote a Deep Learning for Coders book based on my learnings and notes that I took while studying Stanford CS231 CNN, Stanford CS224 NLP, Coursera Machine Learning, and fast.ai courses.
  • Data science Python notebooks - a collection of Jupyter notebooks on machine learning and deep learning. My favorites are “Language modelling in Malay language for downstream NLP tasks”, “Not Hotdog AI Camera mobile app”, “Deep Painterly Harmonization”, and “Guide to TensorFlow + Keras on TPU v2 for free on Google Colab”.
  • PyTorch Mobile Kit is a starter kit app that does Machine Learning on edge from camera output, photos, and videos.
  • Fast.ai Mobile Camera is a guide and demo on “How I Shipped a Neural Network on Android/iOS Phones with PyTorch and Android Studio/Xcode”. The Android app is doing real time object classification. [blog post from my team]
  • PyTorch CapsNet is the first few PyTorch implementation of Capsule Network NIPS 2017 paper by Geoffrey Hinton, et al. This is my first time implementing deep learning research paper from scratch using PyTorch 0.3.0.

First Retreat#

My first educational retreat was in early 2019. I applied and got accepted out of 3000+ applicants in SEA to join Antler as an Entrepreneur-In-Residence (EIR). It’s sort of apprentice program and business retreat for entrepreneurs building startup. Our team failed to secure a pre-seed funding from Antler and my journey as an entrepreneur ended there. This itself is a blog post on its own, next time.

I wasn’t getting hired as a Data Scientist#

After Antler, I went back to my original plan which is, I was trying to switch careers and land a new job as deep learning engineer or data scientist. I failed to break into this role after spending months interviewing. I got rejected multiple times because companies are either not confident in hiring someone like me without a Machine Learning/Deep Learning PhD or someone that cannot recite backpropagation algorithms during coding interview sessions.

Instead of focusing on skills thought to be required of data scientists, we can look at what they have actually done before. (source)

This is the truth?

I know hiring is broken for SWE roles at big tech companies but I never expect this bad. OK, I will stop there. This is not a post to rant about hiring is broken.

To add on, I have not touched data science for a while. Why? In my opinion, if you are not FAANG, it’s costly to scale data science to production grade where your models can work robustly under known adversarial attacks. Data/AI ethics are challenging. Another smaller issue is research is not catching up fast enough with business demands of interpretable models. A lot more has been said on those issues. Check out “Rebooting AI” by Gary Marcus. Another example is this “Data Science: Reality Doesn’t Meet Expectations” post.

I’m sorry to say that I don’t have solutions now. I will consider to give data science another go in the future.

Why did you join code retreat?#

I found out about RC in a period of my career when I wasn’t growing much. I was frequently the sole software engineer on my team and frequently worked on projects on my own.

The Second Code Retreat#

I’ve moved on from pursuing Data Scientist role. So I started my journey into the second educational retreat phase. The startup company that runs the code retreat is flexible enough that allows us to design and customize plan for our retreat. My design mainly drew inspirations from Recurse Center User’s Manual and my experience organizing and running virtual/in-person study groups for fast.ai and AI Saturdays students. I also referenced a few online articles:

The goal and focus of this code retreat is I want to get significantly better at programming and the fundamentals of Computer Science. As Richard Feynman put it:

What I cannot create, I do not understand

And I want to be a wizard :smile: Joke aside, during the COVID-19 crisis, where most of us are stuck at home and work suspended, this is the best time to start doing things you always wanted to do.

What did you do at your code retreat?#

I worked on a variety of projects. Some reached completion, and some didn’t. I did things that I’ve always wanted to do, but also ended up doing things I never thought I would. Some of my favorites (marked as *) are:

  • * Learned a new programming language, Rust. I created a very inaccurate “knowledge map” of stuffs I referenced learning Rust.
  • Learned to make my own version of the classic command line tool grep using Rust.
  • Build a basic multithreaded web server from scratch (zero dependencies) using Rust and along the way, learn a bit about TCP and HTTP.
  • * Implements Conway’s Game of Life in Rust and WebAssembly.
  • Learn Rust by creating too many linked lists.
  • * KVS, a high-performance, networked, parallel and asynchronous key/value store (embedded database like SQLite) developed in Rust.
  • RNNoise is a Rust and Node.js bindings to Xiph’s RNNoise denoising C library.
  • * Deep dive into Xi Editor internals. Xi is a modern editor for the next 20 years. This exercise altered my appreciation for persistent rope data structure.
  • * You Don’t Know Go, Yet book is a project for me to relearn Go from first principles by writing a book on it. I learned advanced Go stuffs in this study.
  • Learn Test-Driven Development (TDD) in Go for the last time.
  • * MinTorrent is yet another minimalistic, zero dependency torrent client, written in Go (golang).
  • * Build yourself a programming language. I created Hou programming language interpreter and compiler from scratch using Go.
  • Snippetbox is a project I created to learn full-stack Go web application development.
  • * Learn distributed systems from first principles by doing MIT 6.824 distributed system labs and reading Designing Data Intensive Applications book (learning-in-progress). I’m currently reading academic papers and reimplementing Raft consensus algorithm.
  • …and more (are you here and looking for project ideas to practice a new programming language?)

A lot of them are focused on Computer Science, systems or Linux concepts and go deep into the concepts.

I will continue in another post going into much more detail on each project.

Lastly, I would to take this opportunity to apologize to anyone who have contacted me and I didn’t response back. It is not in my truest intention as I was beating and recovering from burnout last year.


Hire Cedric Chee#

I’m back on the job market! I just lost my freelance job, and I was living without a stable income for the past 9 months.

Contact info and details here.

That’s it for now. Thank you and take care.


Hire Cedric Chee

Job Hunt

The Dangers of Shiny Technology in Software Engineering

It’s Saturday night here. Tonight, I have a chance to revisit some articles in my reading list. The topic is, “using shiny, untested technology”. Here are a few of my selections:

  1. The Magpie Developer by Jeff Atwood in 2008.

we are too easily distracted by shiny new toys and playthings.

  1. The classic article “Why You Should Never Use MongoDB” by Sarah Mei in 2013.
  • I’ve reread the article in 2020 to remind myself again. Sarah Mei is a thought leader in our industry. This article is gold. The thoughts there have somehow guided me in making some decisions in that era. For example, choosing between SQL or NoSQL database for my past web development works.
  1. Choose boring technology by Dan McKinley.
  • The innovation tokens concept is interesting.

Every time I spin up a new project, I try to answer the following question honestly:

“Am I using this project as an excuse to learn some new technology, or am I trying to solve a problem?”

aaronbrethorst on HN

  1. Boring technology is awesome by John Hyland.

Summary: There are a few universal problems with new technologies that you can count on:

  • APIs can change out from under you.
  • They’re less secure.
  • There’ll be fewer established standards.
  • They lack basic tooling.

When to use shiny tech

  • Side projects
  • Internal tooling
  • When it’s worth it

How to handle the hotness (if you must)

  • Roll it out slowly.
  • Standardize your usage of the new technology.
  1. The boring technology behind a one-person Internet company by Wenbin Fang
  • Technology is usually just a means to an end. I’ve seen so many teams burn so much energy on complicated stacks just to drink kool-aid.
  1. Enough with the microservices by Adam Drake.

“Much has been written on the pros and cons of microservices, but unfortunately I’m still seeing them as something being pursued in a cargo cult fashion in the growth-stage startup world.”

Don’t even consider microservices unless you have a system that’s too complex to manage as a monolith. The majority of software systems should be built as a single monolithic application. Do pay attention to good modularity within that monolith, but don’t try to separate it into separate services.

– Martin Fowler

If you can’t build a well-structured monolith, what makes you think microservices is the answer?

Simon Brown

You are not FAANG (Facebook, Apple, Amazon, Netflix, Google), stop trying to be them!

  1. “Let’s use Kubernetes!” Now you have 8 problems by Itamar Turner-Trauring.

  2. 4 engineering mistakes that kill startups

  • “From chasing shiny tech to scaling too soon, beware these common missteps that can endanger your company”.
  1. Hype Driven Development by Marek Kirejczyk.

  2. Thinking The Impact Of Technical Decision by Didiet Noor.

What is the solution?

In my own opinion, focus on your fundamentals. Understand that, if your foundations aren’t strong and your fundamentals are not clear, then no matter what shiny new things you try to learn, it won’t help you for a long run. So, don’t ignore basic concepts before rushing to any new and shiny technologies.

Thank you for reading.

Happy weekend!

Go is my shell script/scripting language in Linux

Long before, I have this theory: Go compiler is so fast that we can use it like an “interpreted” scripting language.

Yesterday, this wild idea surfaced again when I stumbled upon this Reddit post and discussion:

https://www.reddit.com/r/golang/comments/fcumfu/golang_is_my_new_shell/

Last year, I saw some early attempts like the one in this post by Eyal in 2017:

Story: Writing Scripts with Go.

However, I stop there as I felt the approach is a bit clumsy. And no, I don’t expect Go Authors to officially bake this support in Go tooling.

The post from Reddit yesterday really pique my interest again. So, I continue reading and discovered this great blog post by Cloudflare (2018):

Using Go as a scripting language in Linux.

Next, I proceed to try it out and I found the approach is nearly perfect for my use case.

So, along the way, I learned how Linux executes files. By reading the Linux kernel documentation, kernel support for miscellaneous (your favourite) binary formats.

The binfmt_misc is a script module responsible for parsing shebang lines and executing scripts on the target system.

Who knows that the shebang support is actually implemented in the kernel itself and not in the shell or other daemon/process? At least not me.

Once in a while, I like fiddling with some kernel ‘magic’. Today is the day.

As I think back of today’s tiny experiment, it reminds me again of what important things I’ve forgotten.

One way for me to be a better software engineer, be it for front-end or back-end engineering is to learn the fundamentals (of computer science). That is, to me is the Operating System (OS) like Unix or Linux. Today’s example tells me that almost every time I go back and learn about the Linux kernel, it taught me new things that surprises me.

I suggest, if you have not, you should check The Linux kernel documentation. There are many hidden gems there, like the one I encountered today.

That’s it for today. Thanks for reading.

Have a nice day.

WebAssemby with Rust and Emscripten

I’m learning WebAssemby with Rust and Emscripten.

First, download and install Rust:

cedric@trinine:~/cedric/learn-rust$ curl https://sh.rustup.rs -sSf | sh
info: downloading installer

Welcome to Rust!

This will download and install the official compiler for the Rust programming
language, and its package manager, Cargo.

It will add the cargo, rustc, rustup and other commands to Cargo's bin
directory, located at:

/home/cedric/.cargo/bin

This path will then be added to your PATH environment variable by modifying the
profile file located at:

/home/cedric/.profile

You can uninstall at any time with rustup self uninstall and these changes will
be reverted.

Current installation options:

    default host triple: x86_64-unknown-linux-gnu
    default toolchain: stable
modify PATH variable: yes

1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
>

info: syncing channel updates for 'stable-x86_64-unknown-linux-gnu'
info: latest update on 2019-07-04, rust version 1.36.0 (a53f9df32 2019-07-03)
info: downloading component 'rustc'
info: downloading component 'rust-std'
info: downloading component 'cargo'
info: downloading component 'rust-docs'
info: installing component 'rustc'
91.1 MiB /  91.1 MiB (100 %)  13.8 MiB/s in  6s ETA:  0s
info: installing component 'rust-std'
61.3 MiB /  61.3 MiB (100 %)  16.1 MiB/s in  3s ETA:  0s
info: installing component 'cargo'
info: installing component 'rust-docs'
11.0 MiB /  11.0 MiB (100 %)   6.8 MiB/s in  1s ETA:  0s
info: default toolchain set to 'stable'

stable installed - rustc 1.36.0 (a53f9df32 2019-07-03)


Rust is installed now. Great!

To get started you need Cargo's bin directory ($HOME/.cargo/bin) in your PATH
environment variable. Next time you log in this will be done automatically.

To configure your current shell run source $HOME/.cargo/env

WebAssembly Tutorial#

This section will show you how to build and run your first Rust and WebAssembly program: a Web page that alerts “Hello, World!”

You can follow along the original tutorial here.

cedric@trinine:~/cedric/learn-rust/wasm-game-of-life$ wasm-pack build
[INFO]: Checking for the Wasm target...
info: downloading component 'rust-std' for 'wasm32-unknown-unknown'
info: installing component 'rust-std' for 'wasm32-unknown-unknown'
[INFO]: Compiling to Wasm...
   Compiling proc-macro2 v1.0.1
   Compiling unicode-xid v0.2.0
   Compiling log v0.4.8
   Compiling syn v1.0.3
   Compiling wasm-bindgen-shared v0.2.50
   Compiling cfg-if v0.1.9
   Compiling bumpalo v2.6.0
   Compiling lazy_static v1.3.0
   Compiling wasm-bindgen v0.2.50
   Compiling quote v1.0.2
   Compiling wasm-bindgen-backend v0.2.50
   Compiling wasm-bindgen-macro-support v0.2.50
   Compiling wasm-bindgen-macro v0.2.50
   Compiling console_error_panic_hook v0.1.6
   Compiling wasm-game-of-life v0.1.0 (/home/cedric/learn-rust/wasm-game-of-life)
warning: function is never used: `set_panic_hook`
 --> src/utils.rs:1:1
  |
1 | pub fn set_panic_hook() {
  | ^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: #[warn(dead_code)] on by default

    Finished release [optimized] target(s) in 47.93s
[INFO]: Installing wasm-bindgen...
[INFO]: Optional fields missing from Cargo.toml: 'description', 'repository', and 'license'. These are not necessary, but recommended
[INFO]: :-) Done in 52.62s
[INFO]: :-) Your wasm pkg is ready to publish at ./pkg.
$ npm init wasm-app www
npx: installed 1 in 2.302s
🦀 Rust + 🕸 Wasm = ❤

Publishing to NPM:

cedric@trinine:~/cedric/learn-rust/wasm-game-of-life$ wasm-pack login
Username: *********
Password:
Email: (this IS public) ********
npm ERR! code E401
npm ERR! Incorrect or missing password.
npm ERR! If you were trying to login, change your password, create an
npm ERR! authentication token or enable two-factor authentication then
npm ERR! that means you likely typed your password in incorrectly.
npm ERR! Please try again, or recover your password at:
npm ERR!     https://www.npmjs.com/forgot
npm ERR!
npm ERR! If you were doing some other operation then your saved credentials are
npm ERR! probably out of date. To correct this please try logging in again with:
cedric@trinine:~/cedric/learn-rust/wasm-game-of-life$

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/cedric/.npm/_logs/2019-08-22T11_46_05_314Z-debug.log
Error: Login to registry https://registry.npmjs.org/ failed

# Retry step

$ wasm-pack login
Username: ********
Password:
Email: (this IS public) ***************
Logged in as ********* on https://registry.npmjs.org/.
[INFO]: 👋  logged you in!

cedric@trinine:~/cedric/learn-rust/wasm-game-of-life$ wasm-pack publish
npm notice
npm notice 📦  wasm-game-of-life@0.1.0
npm notice === Tarball Contents ===
npm notice 301B   package.json
npm notice 2.2kB  README.md
npm notice 15.5kB wasm_game_of_life_bg.wasm
npm notice 772B   wasm_game_of_life.d.ts
npm notice 2.8kB  wasm_game_of_life.js
npm notice === Tarball Details ===
npm notice name:          wasm-game-of-life
npm notice version:       0.1.0
npm notice package size:  9.4 kB
npm notice unpacked size: 21.5 kB
npm notice shasum:        f0acf58e757f73b8a615a9b6bb7720823dc6ddad
npm notice integrity:     sha512-hl+J9N+gKAch/[...]hLQJHNyRb/zAQ==
npm notice total files:   5
npm notice
This operation requires a one-time password.
Enter OTP: *********
npm ERR! code E403
npm ERR! 403 Forbidden - PUT https://registry.npmjs.org/wasm-game-of-life - Package name too similar to existing packages; try renaming your package to '@scope/wasm-game-of-life' and publishing with 'npm publish --access=public' instead

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/cedric/.npm/_logs/2019-08-22T13_13_42_232Z-debug.log
Error: Publishing to npm failed
Caused by: failed to execute `npm publish`: exited with exit code: 1

To learn more, you can take a look at the learning resources below:

Examples#

Recent example of projects:

Tools and Libraries#

  • wasmtime - A small, efficient, and standalone JIT-style runtime for WebAssembly and WASI, using Cranelift.

JPEG XL

I’m reading the recently published JPEG XL spec.

Brunsli lossless recompression system and FUIF responsive image is interesting.

So, I go on an exploration and learned a few things about JPEG XL.

We care for making the Internet faster.

Future of image compression with utmost respect to our digital heritage.

My take: I consider Brunsli a very promising approach as the successor of JPEG standard.

Learning WireGuard

A random and wild adventure into the WireGuard land.

What is WireGuard?

Is it the replacement of OpenVPN, IPSec? Is it the future of VPN. I don’t want to do injustice to WireGuard. I think WireGuard website explained it pretty well, even for not very technical people.

Installation for Ubuntu 16.04#

You can see the full glory in the logs/terminal output of this adventure. If you pay enough attention, you will notice that I banged my head trying to get WireGuard kernel module works. To save you from repeating my mistakes, do remember where (which environment: virtualized, VM, container or host) you run your commands; DO NOT install Linux kernel while you are inside Docker container.

Step 0:

Step 1:

Steps and instructions.

  1. Install and setup WireGuard kernel module:
$ sudo add-apt-repository ppa:wireguard/wireguard

$ sudo apt-get update

$ sudo apt install wireguard

Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following additional packages will be installed:
  wireguard-dkms wireguard-tools
The following NEW packages will be installed:
  wireguard wireguard-dkms wireguard-tools
0 upgraded, 3 newly installed, 0 to remove and 2 not upgraded.
Need to get 358 kB of archives.
After this operation, 2,042 kB of additional disk space will be used.
Do you want to continue? [Y/n] Y
Get:1 http://ppa.launchpad.net/wireguard/wireguard/ubuntu xenial/main amd64 wireguard-dkms all 0.0.20200215-0ppa1~16.04 [259 kB]
Get:2 http://ppa.launchpad.net/wireguard/wireguard/ubuntu xenial/main amd64 wireguard-tools amd64 1.0.20200206-0ppa1~16.04 [91.6 kB]
Get:3 http://ppa.launchpad.net/wireguard/wireguard/ubuntu xenial/main amd64 wireguard all 1.0.20200206-0ppa1~16.04 [7,334 B]
Fetched 358 kB in 4s (77.5 kB/s)      
Selecting previously unselected package wireguard-dkms.
(Reading database ... 553444 files and directories currently installed.)
Preparing to unpack .../wireguard-dkms_0.0.20200215-0ppa1~16.04_all.deb ...
Unpacking wireguard-dkms (0.0.20200215-0ppa1~16.04) ...
Selecting previously unselected package wireguard-tools.
Preparing to unpack .../wireguard-tools_1.0.20200206-0ppa1~16.04_amd64.deb ...
Unpacking wireguard-tools (1.0.20200206-0ppa1~16.04) ...
Selecting previously unselected package wireguard.
Preparing to unpack .../wireguard_1.0.20200206-0ppa1~16.04_all.deb ...
Unpacking wireguard (1.0.20200206-0ppa1~16.04) ...
Processing triggers for man-db (2.7.5-1) ...
Setting up wireguard-dkms (0.0.20200215-0ppa1~16.04) ...
Loading new wireguard-0.0.20200215 DKMS files...
First Installation: checking all kernels...
Building for 4.4.0-173-generic and 4.9.12-040912-generic
Building initial module for 4.4.0-173-generic
Secure Boot not enabled on this system.
Done.

wireguard:
Running module version sanity check.
 - Original module
   - No original module exists within this kernel
 - Installation
   - Installing to /lib/modules/4.4.0-173-generic/updates/dkms/

depmod....

DKMS: install completed.
Setting up wireguard-tools (1.0.20200206-0ppa1~16.04) ...
Setting up wireguard (1.0.20200206-0ppa1~16.04) ...
  1. The following problem troubleshooting steps (lsmod kernel) are from: https://www.kevin-messer.net/how-to-setup-a-vpn-on-ubuntu-19-10-using-wireguard/

As Wireguard is a kernel module, soon to be mainlined (included inside the Linux kernel), you will need to check if it’s enabled and enable it if it’s not.

To check if you will have to do so:

$ lsmod | grep wireguard

If it’s ok, you will get something like this:

root@starlite:~# lsmod | grep wireguard
wireguard             208896  0
ip6_udp_tunnel         16384  1 wireguard
udp_tunnel             16384  1 wireguard

If you get nothing, you will have to enable it:

$ modprobe wireguard
modprobe: ERROR: could not insert 'wireguard': Operation not permitted

If you encountered error, run in verbose mode to check log:

root@starlite:~$ modprobe -vvv wireguard                                                                    
modprobe: INFO: ../libkmod/libkmod.c:364 kmod_set_log_fn() custom logging function 0x559bfb4c2750 registered                                                       
modprobe: DEBUG: ../libkmod/libkmod-index.c:755 index_mm_open() file=/lib/modules/4.15.0-1057/modules.dep.bin                 
modprobe: DEBUG: ../libkmod/libkmod-index.c:755 index_mm_open() file=/lib/modules/4.15.0-1057/modules.alias.bin                    
modprobe: DEBUG: ../libkmod/libkmod-index.c:755 index_mm_open() file=/lib/modules/4.15.0-1057/modules.symbols.bin              
modprobe: DEBUG: ../libkmod/libkmod-index.c:755 index_mm_open() file=/lib/modules/4.15.0-1057/modules.builtin.bin                     
modprobe: DEBUG: ../libkmod/libkmod-module.c:556 kmod_module_new_from_lookup() input alias=wireguard, normalized=wireguard             
modprobe: DEBUG: ../libkmod/libkmod-module.c:562 kmod_module_new_from_lookup() lookup modules.dep wireguard                                
modprobe: DEBUG: ../libkmod/libkmod.c:574 kmod_search_moddep() use mmaped index 'modules.dep' modname=wireguard                 
modprobe: DEBUG: ../libkmod/libkmod.c:402 kmod_pool_get_module() get module name='wireguard' found=(nil)                         
modprobe: DEBUG: ../libkmod/libkmod.c:410 kmod_pool_add_module() add 0x559bfd41c050 key='wireguard'                            
modprobe: DEBUG: ../libkmod/libkmod.c:402 kmod_pool_get_module() get module name='ip6_udp_tunnel' found=(nil)                                       
modprobe: DEBUG: ../libkmod/libkmod.c:402 kmod_pool_get_module() get module name='ip6_udp_tunnel' found=(nil)                                            
modprobe: DEBUG: ../libkmod/libkmod.c:410 kmod_pool_add_module() add 0x559bfd41c180 key='ip6_udp_tunnel'                                       
modprobe: DEBUG: ../libkmod/libkmod-module.c:196 kmod_module_parse_depline() add dep: /lib/modules/4.15.0-1057/kernel/net/ipv6/ip6_udp_tunnel.ko  
modprobe: DEBUG: ../libkmod/libkmod.c:402 kmod_pool_get_module() get module name='udp_tunnel' found=(nil)                                             
modprobe: DEBUG: ../libkmod/libkmod.c:402 kmod_pool_get_module() get module name='udp_tunnel' found=(nil)
modprobe: DEBUG: ../libkmod/libkmod.c:410 kmod_pool_add_module() add 0x559bfd41c2b0 key='udp_tunnel'                                                  
modprobe: DEBUG: ../libkmod/libkmod-module.c:196 kmod_module_parse_depline() add dep: /lib/modules/4.15.0-1057/kernel/net/ipv4/udp_tunnel.ko
modprobe: DEBUG: ../libkmod/libkmod-module.c:202 kmod_module_parse_depline() 2 dependencies for wireguard                                                
modprobe: DEBUG: ../libkmod/libkmod-module.c:583 kmod_module_new_from_lookup() lookup wireguard=0, list=0x559bfd41c160                         
modprobe: DEBUG: ../libkmod/libkmod.c:501 lookup_builtin_file() use mmaped index 'modules.builtin' modname=wireguard               
modprobe: DEBUG: ../libkmod/libkmod-module.c:1750 kmod_module_get_initstate() could not open '/sys/module/wireguard/initstate': No such file or directory
modprobe: DEBUG: ../libkmod/libkmod-module.c:1760 kmod_module_get_initstate() could not open '/sys/module/wireguard': No such file or directory
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=snd_pcsp mod->name=udp_tunnel mod->alias=(null)        
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=snd_usb_audio mod->name=udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=cx88_alsa mod->name=udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=snd_atiixp_modem mod->name=udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=snd_intel8x0m mod->name=udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=snd_via82xx_modem mod->name=udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=md_mod mod->name=udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=bonding mod->name=udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=dummy mod->name=udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=nvme_core mod->name=udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod.c:501 lookup_builtin_file() use mmaped index 'modules.builtin' modname=udp_tunnel
modprobe: DEBUG: ../libkmod/libkmod-module.c:1750 kmod_module_get_initstate() could not open '/sys/module/udp_tunnel/initstate': No such file or directory
modprobe: DEBUG: ../libkmod/libkmod-module.c:1760 kmod_module_get_initstate() could not open '/sys/module/udp_tunnel': No such file or directory
modprobe: DEBUG: ../libkmod/libkmod-module.c:744 kmod_module_get_path() name='udp_tunnel' path='/lib/modules/4.15.0-1057/kernel/net/ipv4/udp_tunnel.ko'
modprobe: DEBUG: ../libkmod/libkmod-module.c:744 kmod_module_get_path() name='udp_tunnel' path='/lib/modules/4.15.0-1057/kernel/net/ipv4/udp_tunnel.ko'
insmod /lib/modules/4.15.0-1057/kernel/net/ipv4/udp_tunnel.ko
modprobe: DEBUG: ../libkmod/libkmod-module.c:744 kmod_module_get_path() name='udp_tunnel' path='/lib/modules/4.15.0-1057/kernel/net/ipv4/udp_tunnel.ko'
modprobe: INFO: ../libkmod/libkmod-module.c:886 kmod_module_insert_module() Failed to insert module '/lib/modules/4.15.0-1057/kernel/net/ipv4/udp_tunnel.ko': Operation not permitted
modprobe: ERROR: could not insert 'wireguard': Operation not permitted
modprobe: DEBUG: ../libkmod/libkmod-module.c:468 kmod_module_unref() kmod_module 0x559bfd41c050 released
modprobe: DEBUG: ../libkmod/libkmod.c:418 kmod_pool_del_module() del 0x559bfd41c050 key='wireguard'
modprobe: DEBUG: ../libkmod/libkmod-module.c:468 kmod_module_unref() kmod_module 0x559bfd41c2b0 released
modprobe: DEBUG: ../libkmod/libkmod.c:418 kmod_pool_del_module() del 0x559bfd41c2b0 key='udp_tunnel'
modprobe: DEBUG: ../libkmod/libkmod-module.c:468 kmod_module_unref() kmod_module 0x559bfd41c180 released
modprobe: DEBUG: ../libkmod/libkmod.c:418 kmod_pool_del_module() del 0x559bfd41c180 key='ip6_udp_tunnel'
modprobe: INFO: ../libkmod/libkmod.c:331 kmod_unref() context 0x559bfd41b490 released

Retry running command with sudo:

ubuntu@starlite:~$ sudo modprobe -vvv
modprobe: INFO: ../libkmod/libkmod.c:364 kmod_set_log_fn() custom logging function 0x55a9e2d4c750 registered                                                       
modprobe: DEBUG: ../libkmod/libkmod-index.c:755 index_mm_open() file=/lib/modules/4.15.0-1057/modules.dep.bin                                                  
modprobe: DEBUG: ../libkmod/libkmod-index.c:755 index_mm_open() file=/lib/modules/4.15.0-1057/modules.alias.bin
modprobe: DEBUG: ../libkmod/libkmod-index.c:755 index_mm_open() file=/lib/modules/4.15.0-1057/modules.symbols.bin                                              
modprobe: DEBUG: ../libkmod/libkmod-index.c:755 index_mm_open() file=/lib/modules/4.15.0-1057/modules.builtin.bin             
modprobe: DEBUG: ../libkmod/libkmod-module.c:556 kmod_module_new_from_lookup() input alias=wireguard, normalized=wireguard             
modprobe: DEBUG: ../libkmod/libkmod-module.c:562 kmod_module_new_from_lookup() lookup modules.dep wireguard                        
modprobe: DEBUG: ../libkmod/libkmod.c:574 kmod_search_moddep() use mmaped index 'modules.dep' modname=wireguard                           
modprobe: DEBUG: ../libkmod/libkmod.c:402 kmod_pool_get_module() get module name='wireguard' found=(nil)                               
modprobe: DEBUG: ../libkmod/libkmod.c:410 kmod_pool_add_module() add 0x55a9e48c1010 key='wireguard'                                        
modprobe: DEBUG: ../libkmod/libkmod.c:402 kmod_pool_get_module() get module name='ip6_udp_tunnel' found=(nil)                   
modprobe: DEBUG: ../libkmod/libkmod.c:402 kmod_pool_get_module() get module name='ip6_udp_tunnel' found=(nil)                    
modprobe: DEBUG: ../libkmod/libkmod.c:410 kmod_pool_add_module() add 0x55a9e48c1140 key='ip6_udp_tunnel'                       
modprobe: DEBUG: ../libkmod/libkmod-module.c:196 kmod_module_parse_depline() add dep: /lib/modules/4.15.0-1057/kernel/net/ipv6/ip6_udp_tunnel.ko
modprobe: DEBUG: ../libkmod/libkmod.c:402 kmod_pool_get_module() get module name='udp_tunnel' found=(nil)                                                
modprobe: DEBUG: ../libkmod/libkmod.c:402 kmod_pool_get_module() get module name='udp_tunnel' found=(nil)                                      
modprobe: DEBUG: ../libkmod/libkmod.c:410 kmod_pool_add_module() add 0x55a9e48c1270 key='udp_tunnel'                                                  
modprobe: DEBUG: ../libkmod/libkmod-module.c:196 kmod_module_parse_depline() add dep: /lib/modules/4.15.0-1057/kernel/net/ipv4/udp_tunnel.ko      
modprobe: DEBUG: ../libkmod/libkmod-module.c:202 kmod_module_parse_depline() 2 dependencies for wireguard
modprobe: DEBUG: ../libkmod/libkmod-module.c:583 kmod_module_new_from_lookup() lookup wireguard=0, list=0x55a9e48c1120                                
modprobe: DEBUG: ../libkmod/libkmod.c:501 lookup_builtin_file() use mmaped index 'modules.builtin' modname=wireguard
modprobe: DEBUG: ../libkmod/libkmod-module.c:1750 kmod_module_get_initstate() could not open '/sys/module/wireguard/initstate': No such file or directory
modprobe: DEBUG: ../libkmod/libkmod-module.c:1760 kmod_module_get_initstate() could not open '/sys/module/wireguard': No such file or directory
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=snd_pcsp mod->name=udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=snd_usb_audio mod->name=udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=cx88_alsa mod->name=udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=snd_atiixp_modem mod->name=udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=snd_intel8x0m mod->name=udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=snd_via82xx_modem mod->name=udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=md_mod mod->name=udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=bonding mod->name=udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=dummy mod->name=udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=nvme_core mod->name=udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod.c:501 lookup_builtin_file() use mmaped index 'modules.builtin' modname=udp_tunnel
modprobe: DEBUG: ../libkmod/libkmod-module.c:1750 kmod_module_get_initstate() could not open '/sys/module/udp_tunnel/initstate': No such file or directory
modprobe: DEBUG: ../libkmod/libkmod-module.c:1760 kmod_module_get_initstate() could not open '/sys/module/udp_tunnel': No such file or directory
modprobe: DEBUG: ../libkmod/libkmod-module.c:744 kmod_module_get_path() name='udp_tunnel' path='/lib/modules/4.15.0-1057/kernel/net/ipv4/udp_tunnel.ko'
modprobe: DEBUG: ../libkmod/libkmod-module.c:744 kmod_module_get_path() name='udp_tunnel' path='/lib/modules/4.15.0-1057/kernel/net/ipv4/udp_tunnel.ko'
insmod /lib/modules/4.15.0-1057/kernel/net/ipv4/udp_tunnel.ko
modprobe: DEBUG: ../libkmod/libkmod-module.c:744 kmod_module_get_path() name='udp_tunnel' path='/lib/modules/4.15.0-1057/kernel/net/ipv4/udp_tunnel.ko'
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=snd_pcsp mod->name=ip6_udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=snd_usb_audio mod->name=ip6_udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=cx88_alsa mod->name=ip6_udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=snd_atiixp_modem mod->name=ip6_udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=snd_intel8x0m mod->name=ip6_udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=snd_via82xx_modem mod->name=ip6_udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=md_mod mod->name=ip6_udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=bonding mod->name=ip6_udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=dummy mod->name=ip6_udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=nvme_core mod->name=ip6_udp_tunnel mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod.c:501 lookup_builtin_file() use mmaped index 'modules.builtin' modname=ip6_udp_tunnel
modprobe: DEBUG: ../libkmod/libkmod-module.c:1750 kmod_module_get_initstate() could not open '/sys/module/ip6_udp_tunnel/initstate': No such file or directory
modprobe: DEBUG: ../libkmod/libkmod-module.c:1760 kmod_module_get_initstate() could not open '/sys/module/ip6_udp_tunnel': No such file or directory
modprobe: DEBUG: ../libkmod/libkmod-module.c:744 kmod_module_get_path() name='ip6_udp_tunnel' path='/lib/modules/4.15.0-1057/kernel/net/ipv6/ip6_udp_tunnel.ko'
modprobe: DEBUG: ../libkmod/libkmod-module.c:744 kmod_module_get_path() name='ip6_udp_tunnel' path='/lib/modules/4.15.0-1057/kernel/net/ipv6/ip6_udp_tunnel.ko'
insmod /lib/modules/4.15.0-1057/kernel/net/ipv6/ip6_udp_tunnel.ko
modprobe: DEBUG: ../libkmod/libkmod-module.c:744 kmod_module_get_path() name='ip6_udp_tunnel' path='/lib/modules/4.15.0-1057/kernel/net/ipv6/ip6_udp_tunnel.ko'
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=snd_pcsp mod->name=wireguard mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=snd_usb_audio mod->name=wireguard mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=cx88_alsa mod->name=wireguard mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=snd_atiixp_modem mod->name=wireguard mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=snd_intel8x0m mod->name=wireguard mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=snd_via82xx_modem mod->name=wireguard mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=md_mod mod->name=wireguard mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=bonding mod->name=wireguard mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=dummy mod->name=wireguard mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1393 kmod_module_get_options() modname=nvme_core mod->name=wireguard mod->alias=(null)
modprobe: DEBUG: ../libkmod/libkmod-module.c:1750 kmod_module_get_initstate() could not open '/sys/module/wireguard/initstate': No such file or directory
modprobe: DEBUG: ../libkmod/libkmod-module.c:1760 kmod_module_get_initstate() could not open '/sys/module/wireguard': No such file or directory
modprobe: DEBUG: ../libkmod/libkmod-module.c:744 kmod_module_get_path() name='wireguard' path='/lib/modules/4.15.0-1057/updates/dkms/wireguard.ko'
modprobe: DEBUG: ../libkmod/libkmod-module.c:744 kmod_module_get_path() name='wireguard' path='/lib/modules/4.15.0-1057/updates/dkms/wireguard.ko'
insmod /lib/modules/4.15.0-1057/updates/dkms/wireguard.ko
modprobe: DEBUG: ../libkmod/libkmod-module.c:744 kmod_module_get_path() name='wireguard' path='/lib/modules/4.15.0-1057/updates/dkms/wireguard.ko'
modprobe: DEBUG: ../libkmod/libkmod-module.c:468 kmod_module_unref() kmod_module 0x55a9e48c1010 released
modprobe: DEBUG: ../libkmod/libkmod.c:418 kmod_pool_del_module() del 0x55a9e48c1010 key='wireguard'
modprobe: DEBUG: ../libkmod/libkmod-module.c:468 kmod_module_unref() kmod_module 0x55a9e48c1270 released
modprobe: DEBUG: ../libkmod/libkmod.c:418 kmod_pool_del_module() del 0x55a9e48c1270 key='udp_tunnel'
modprobe: DEBUG: ../libkmod/libkmod-module.c:468 kmod_module_unref() kmod_module 0x55a9e48c1140 released
modprobe: DEBUG: ../libkmod/libkmod.c:418 kmod_pool_del_module() del 0x55a9e48c1140 key='ip6_udp_tunnel'
modprobe: INFO: ../libkmod/libkmod.c:331 kmod_unref() context 0x55a9e48c0450 released

Modprobe run looks fine. FINALLY!!!??? Let’s verify.

$ lsmod | grep wireguard
wireguard             221184  0
ip6_udp_tunnel         16384  1 wireguard
udp_tunnel             16384  1 wireguard

Now you’re ready to get to the next step.

Using Ubuntu as a Client with WireGuard#

WireGuard server was provisoned by Trails of Bits’ Algo VPN.

  1. Locate the config file and configure WireGuard:
$ cd ~/dev/work/repo/algo

$ sudo install -o root -g root -m 600 configs/{algo_server_pub_ip}/wireguard/zen.conf /etc/wireguard/wg0.conf
  1. Start WireGuard client using systemd:
$ sudo systemctl start wg-quick@wg0
  1. Check WireGuard client status:
$ sudo systemctl status wg-quick@wg0
  1. Verify WireGuard client connection to server:
$ sudo wg

interface: wg0
  public key: **********sIV0RZ*******=
  private key: (hidden)
  listening port: 52180

peer: ***********seEYg3*********=
  preshared key: (hidden)
  endpoint: {server public ip}:port
  allowed ips: 10.x.x.2/32
  latest handshake: 21 seconds ago
  transfer: 8.88 KiB received, 14.36 KiB sent
  1. Check for IP leak.

The WireGuard client IP should be the same as the WireGuard server IP, not the public IP assigned by your ISP. Otherwise, your connection is leaking your ISP IP and to some extent, may even be leaking DNS requests.

$ curl http://zx2c4.com/ip
  1. Configure WireGuard to auto-restart connection across OS restart:
$ sudo systemctl enable wg-quick@wg0

That’s all! Happy hacking…

Why Ubuntu

Six reasons why developers choose Ubuntu Desktop#

Finally, I have some downtime to sit down and think about this topic. Before I get to the key reasons, I’ll start with a little story of mine.

I’m a long time Ubuntu user for both desktop and server. Before Ubuntu, my first Linux OS is Debian. That was during my college time (2000) and during some courses related to programming and OS. I dabbled with Linux kernel not long after.

This journey opened up and introduced me to other Linux distros like Red Hat and Gentoo. Along this journey, I distro hop a lot that I lost count now. But that didn’t stop until around 2010, when I fully migrated to new laptop and installed Ubuntu Desktop.

Back then, the timing was perfect — the combo of hardware support from mainline linux kernel is almost perfect and software support from Ubuntu Desktop is usable. So, over the course of several months, I also migrated my homelab and staging environment servers to use Ubuntu Server. Since then, I only use Ubuntu Server in a small number of production workloads. Now, Ubuntu is default choice for new deployments.

When I just started learning Ubuntu, I have a clear strategy for the next 3 to 5 years and that is, I need an OS that is geared for productivity — supports efficient workflow from development to production. From the whitepaper, this strategy is aligned to “Consistent OS experience across platforms”.

Without further ado, below is the highlights from the whitepaper.

First choice for artificial intelligence and machine learning#

  • GPUs have changed the face of AI, and NVIDIA is investing in CUDA on Linux to unleash the power of their latest graphics cards for general computing.
  • Canonical has also worked with Google to develop Kubeflow, a solution for rapidly building composable, portable, and scalable machine learning stacks.

Consistent OS experience across platforms#

  • Arguably, the greatest advantage of developing on Ubuntu is that it enables users to work with the exact same underlying operating system on their desktops as they do on their servers, in the cloud, and on IoT devices.
  • This consistent Ubuntu experience makes it easy to test locally before deploying globally – providing developers with a smooth path from development to production, with the same software running on both their desktop and target production environment.

Streamlined distribution through snaps#

  • For developers targeting Linux, snaps offer an ideal way to package and distribute applications. Snaps are containerised applications that work on desktops, cloud, and IoT devices.
  • They are simple to create and install, safe to run, and update automatically. And because snaps are packaged with all their dependencies, they work on all major Linux systems without modification.

Hardware and software freedom#

  • The level of hardware and software support is essential for a smooth development process. Without it, developers risk having to spend an inordinate amount of time fixing compatibility issues before they can even make progress on their applications.
  • Ubuntu’s flexibility will help to reduce friction and accelerate development, eliminating issues that might otherwise arise when selecting components and solutions.

Extensive support – from Canonical and the Ubuntu community#

  • It doesn’t matter how compelling an operating system’s other features are if developers cannot rely on it to be stable, secure, and continuously updated. That is why Ubuntu LTS (Long Term Support) releases benefit from five years of support from Canonical – with critical bug fixes, security updates, and hardware enablement – at no cost.
  • Ubuntu users can even apply critical kernel security fixes without rebooting their systems thanks to the Canonical Livepatch Service, helping to minimise downtime while maintaining compliance and security.
  • And for those seeking to add value to their Ubuntu deployments and achieve a greater level of support and peace of mind, there is the option of Ubuntu Advantage – the commercial support package from Canonical.

Certified hardware#

  • Customers can be confident that their PCs will work flawlessly with Ubuntu right out of the box, with no need to spend time on installation.

I am Linux distro agnostic though. Choices are good. I’ll pick the distro that fit our requirements. So, it’s not a zero sum competition. Improve or you get left behind :D

Technical Writing

Making the world a clearer place.

Awesome collection of learning resources, articles, tools, and more for technical writing and documentation project.

Courses and learning resources#

Articles, Blog Posts, Tips#

There is a secret that needs to be understood in order to write good software documentation: there isn’t one thing called documentation, there are four. ~ Daniele Procida

Tools#

Movement#

Learn in public#

What is Software Engineering?

Have you ever ponder what is software engineering after all? I did at the start of a new year.

Software engineering from my viewpoint is about:

We see great engineering as the reduction of complex things into simple things. These initial complexities might include code, abstractions, systems, architecture or infrastructure. We believe this approach, searching for simplicity in complex things, lies at the heart of problem solving.