How to migrate WhatsApp from iPhone and Mac to Android

It’s actually not that hard to transfer WhatsApp Chats from iPhone to Android, but scraping together all the information to do so has cost me half a day. Half a day I want to spare you.

Disclaimer: It will cost you 7$ as of December 2019 because you’ll need the wazzapmigrator, to which I don’t have any affiliation whatsoever. Also, don’t try to rush the process. It required me a whole day to do it, of which I actually spent around 3h actually doing stuff to migrate. It might be less for you if you follow this tutorial, but you still shouldn’t expect the whole thing to be super easy…

Furthermore, you’ll need to grant wmextractor (a tool provided by wazzapmigrator) full disk access, which you might not feel comfortable with. I cannot vouch for wazzapmigrator that it’s unproblematic, but to me it seemed trustworthy enough that I went through with the process.

1. Plug in your iPhone, open iTunes and perform a full manual unencrypted backup

If you’re just changing the setting from “iCloud” to “This Computer” it might take a while until “Encrypt local backup” is not greyed out anymore. Then you can uncheck it.

2. Get the wmextractor for Mac here: Currently this page seems to be very badly indexed by Google and it’s also hard to find it through the wazzapmigrator page, so finding this tool is almost what took me the longest.

3. Once you’ve got the wmextractor, you can run the application. Unfortunately you’ll have to give it full disk access. How you can do this is described in detail here: Once you’ve done that, WazzapMigrator Extractor should be able to find the ChatStorage.sqlite file inside your backup. It should look like this:

Select that file and click extract. You’ll be asked whether you want to upload to gdrive and I selected that method. This either took very long or I missed the “completed” dialog for quite some time. Anyways, in the end it worked and I could upload everything to GDrive:

4. Buy and install the Wazzap Extractor app on your Android phone. If you’ve chosen to migrate through GDrive, follow the description here: . In case of complications, such as WhatsApp being installed on the phone or WhatsApp having access to the GDrive app, the Wazzap App will nicely guide you through the steps you’ll have to undertake. Make sure to have both, media (images and so on) as well as ChatStorage downloaded. Afterwards the extraction begins. This will stay on 0% for quite a while. I finally closed the app and opened it again and then somehow it was done… Afterwards it requests you to download a specific version of WhatsApp, which I did. It also tells you to update WhatsApp to the newest version afterwards, but I couldn’t find where to do that and auto backup was turned on, so I left out that step.

The final result: All my data was migrated! Pretty neat.

P.S.: It also marked all of my chat groups as unread and I had to mark them not as read, but hey, that’s not too bad.

Maven and Gradle Dependencies Compared

There is no exact match between dependencies in Maven and Gradle, however here’s a rough mapping:


You can auto-convert your dependencies from Gradle to Maven here:


iCloud vs GDrive vs Dropbox vs AWS Glacier

I’ve compiled a small table for myself to check what’s best suited for me. Thought I’d share if someone tries to make the same decision.

iCloud GDrive Dropbox AWS Glacier
Possible to store in Cloud but not on local machine? Yes (append .nosync to file or folder in iCloud-WebView) Yes (pref -> google drive -> only sync these folders…) Yes Yes
Share Files by Link Yes (“Add People”, works local and in webview) Yes Yes No
Price for Storing 100 GB / month 3 2 10 0.5
Price for Storing 200 GB / month 3 10 10 1
Price for Storing 1 TB / month 10 10 10 5
Price for Storing 2 TB / month 10 20 20 10
Product Discontinuation Risk Low Low Medium Low
Tool for managing on Mac iCloud Pref Pane, Context Menu, iCloud Drive Backup and Sync App Dropbox App Freeze for Mac ($30) or CrossFTP
Configurable which folders to sync Semi: iCloudDrive synced (if selected), also an option to sync “Desktop & Documents” Yes (pref -> google drive -> only sync these folders…) No (just the dropbox folder is synced)
Possible to sync files? Yes Yes Yes No
Retrieval costs / 100 GB 0 0 0 $10
Lock In High Medium Medium Huge (because retrieval is so expensive)
Need to switch when I switch to Linux? Yes No No No
Quality of the Web Interface Medium High Don’t know Low


Most things with iCloud are fine and the functionality to backup your iPhone and sync photos when you’re a Mac user are of course cool. However, it’s not the best solution if you want to free disk space on your mac, since everything on iCloud is also synced on your Mac. You could use the ‘myfolder.nosync’ trick, but since the web-interface of iCloud isn’t that good it’s a bit problematic. For example, if you have a lot of photos, that you’d like to have in the cloud only and you have folders like 2015, 2016, 2017, 2018 on your local machine, each with sub folders of events. Then moving this to iCloud is a bit an awkward process, since you can’t just drag and drop folders to iCloud (it’ll say that’s not possible through the web interface). So you’ll have to move those directories locally to iCloud, and then add the nosync extension. If you had the photos on an external harddrive and they consume more disk space than you have on your machine, you’re a bit out of luck.


I use Gdrive every day for my work, including Docs, Sheets, Drawings and everything. The web interface sometimes is a bit slow, but all in all solid. You can also upload directories, so it would be easy to move things from an external drive to GDrive by drag and drop.


I really don’t have much experience with Dropbox, so I’ll leave this part to someone else.

AWS Glacier

For the love of god, don’t use this as your personal storage solution. Downloading stuff is really slow (5h queue) and expensive, so not worth it unless you know what you’re doing!


For me as a Mac User a good setup is the following combo: get the 200 GB from iCloud to back up my Desktop, Documents, Photos and my Phone. To free disk space on my Mac, I’ll consider purchasing more space in GDrive, once I need it.

Caching with Gradle in Gitlab with AWS Autoscale

So you have a Continuous Integration Server that runs some build tasks. For example, you’re running ./gradlew buildTaskOne. Now, if you run ./gradlew buildTaskOne again you don’t want it to build again if nothing has changed, you just want it to say “up-to-date” and then move on to the next task. On your local machine this goal is achieved relatively straightforward. For Java builds this works out of the box, for other builds a small setup like the following is required:

task installNodeModules(type: Exec) {
    commandLine 'npm', 'i'
    inputs.files 'package.json'
    outputs.dir 'node_modules'

The fist time this task is ran this is executed, the second time it is skipped:

That was easy! So how can you achieve the same thing when using gitlab runners in combination with AWS Autoscale? Some more steps are required to achieve this…

Set Up Distributed S3 Caches

AWS Autoscale uses distinct EC2 instances for each build stage that are dynamically spun up and torn down. This means that for sharing caches, we need a central file store. And what could be better suited for this task than S3? Also, Gitlab integrates really well with this. Simply put the following in your config.toml of your AWS Autoscale manager (bastion server):

concurrent = 10
check_interval = 0
log_level = "debug"

  name = "gitlab-aws-autoscaler"
  url = "<your url>"
  token = "${RUNNER_TOKEN}"
  executor = "docker+machine"
  limit = 6
    image = "taskbase/gitlabrunner:2.4"
    privileged = true
    disable_cache = true
    volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache", "/builds:/builds"]
    Type = "s3"
    ServerAddress = ""
    AccessKey = "${AWS_ACCESS_KEY_ID}"
    SecretKey = "${AWS_SECRET_ACCESS_KEY}"
    BucketName = "your-bucket-name"
    BucketLocation = "eu-central-1"
    Shared = true

The bold part is interesting for you, I’ve added the rest to give you a bit more context. The red bits need to be adjusted to your setup. You’ll need to create an S3 bucket for this to work first.

Set Up Caches in .gitlab-ci.yml

You’ll also need to specify what you want to cache in .gitlab-ci.yml. This is minimal setup for the node_modules example:

  - .gradle
  - ./node_modules # important: don't forget to cache the output!

Those directories will get uploaded to S3. Now I’m not entirely sure about this, but I think the directories have to be within your repository. For example I tried to specify an absolute path /opt/gradle/caches but this didn’t get uploaded! In order to store gradle caches into the .gradle folder, run the following line in a before script part of gitlab ci:

- export GRADLE_USER_HOME=`pwd`/.gradle # there is no $USER_HOME, and /opt/gradle/caches can't be uploaded, so local gradle home is chosen

Explanation for the above setup (optional reading)

On AWS Autoscale runners you don’t have a $USER_HOME variable and gradle seems to store caches by default into /opt/gradle/caches. You can verify this with the following gradle task:

task showMeCache << {
    configurations.compile.each { println it }

But when trying to store the cache at /opt/gradle/caches nothing seems to happen, so a local gradle home is chosen. This can be achieved by setting the GRADLE_USER_HOME variable to a location within your repo. That’s what the command in the before script is good for.

It’s also important to cache the entire .gradle folder and not just ./gradle/caches/ since to check whether something is up-to-date gradle checks the file hashes in ./gradle/4.6/fileHashes, at least in gradle version 4.6. I learned this the hard way by running a git diff on the .gradle folder before and after making a change to the file declared in input.files.

Setting Up the build.gradle File

Another important concept when setting up the gradle caching mechanisms is “path sensitivity”. By default, the path sensitivity is ABSOLUTE and not RELATIVE, meaning that when you have a cache at /my/path/bla and then /your/path/bla the cache will NOT take effect. From what I’ve seen the builds on AWS Autoscale for the same branches have the same absolute path so it’s not a problem, but to be sure you could write something like the following:

FileTree packageJSON() {
    return fileTree('./package.json')
task npmInstall (type: Exec) {
    inputs.files packageJSON()
    outputs.dir 'node_modules'
    commandLine 'npm', 'i'


task npmInstall (type: Exec) {
    inputs.files 'package.json' withPathSensitivity(PathSensitivity.RELATIVE)
    outputs.dir 'node_modules'
    commandLine 'npm', 'i'

Of course, only apply this if your input files are really path independent. But you can also leave this away and introduce it once necessary.

Checking if Everything is Working

For some reason, the cache doesn’t seem to kick in the first time it should. So when we push changes, and then on the second run it should be cached, the caches somehow don’t work yet. But on the third run, they do work.


What is minimally required to successfully employ input / output caches are the following steps:

  • S3 distributed cache setup correctly
  • Gradle Home within repository, tried and tested with .gradle
  • Upload gradle home folder to S3
  • Upload outputs of build task to S3 as well. The build task needs an output in order be be cachable.
  • Try at least two times to check if caching works, the first time when the caches already should kick in, they don’t work for some reason…

Traefik: “Bad Gateway” for Docker Service on a Swarm Node – what to do?

I recently ran into the problem, that suddenly one of my nodes had a “nervous breakdown” and it wasn’t accessible through traefik anymore. All that was printed on the page a “Bad Gateway”. But I was sure that it was working before, I didn’t change any settings.

I tried various solutions including adding a new overlay network to traefik and the services on the problematic node. Nothing would work. So in the end I drained and restarted the node, after which the problem was gone. The commands to do so on an Ubuntu instance would be:

docker node ls # to find the name/id of the node
docker node update --availability drain worker1

# after all containers are removed
sudo reboot

Afterwards, you can simply rescale/redeploy some of your services to repopulate the instance. There currently isn’t any “rebalance” command, because the Docker team doesn’t like the idea of killing healthy containers too much.

StackOverflow Sorting is Broken

StackOverflow sorting sucks.

  1. The accepted answer is always on top. This is a joke. The asker of the question usually accepts the first best thing and then heads off. Thousands of people to come will then see the first best answer on top. For example: . How ridiculous is that?
  2. Below the accepted answer, it’s sorted by upvotes. That system is also broken, because new answers will have a very hard time of getting more upvotes.

The solution? A smart sorting algorithm, like on Quora or Reddit.

There’s an ongoing discussion on StackOverflow about this:

So, StackOverflow, are you ever going to change this?

The Ultimate Mustache Tutorial

Learning Mustache is a lot faster if you understand properly what it’s all about and learn the underlying concepts. You’ll grasp a firmer understanding of Mustache by going through this tutorial, than by directly diving into the official docs, which often seem confusing at first.

What is Mustache?

The first thing to learn is that Mustache is NOT a templating engine. Mustache is a specification for a templating language. This specification can be found here, but don’t read that just yet. You’re not ready yet.

The idea is, that you write templates that adhere to the Mustache specification, that then can be compiled / rendered to create an output.

What’s a Templating Language and what’s it for?

Let’s say you want to send the same email to 100 people, except you want to personalize the salutation with their respective first names. You could replace the first name by some placeholder:

Dear *|FNAME|*

I hearby blablabla ...

Here I have invented (=stolen it from Mailchimp) a syntax for a placeholder: *|FNAME|*

The mustache syntax looks a bit different. This case would be covered by the following template:

Dear {{fname}}

I hearby blablabla ...

As you can see, in Mustache we have curly braces for placeholders, which look like a mustache when rotated 90 degrees clockwise:

Hence the name of the language. There’s more to the Mustache than that, but before diving any deeper into the language, it’s useful to first cover another concept.

By simply having specified a templating language and having written a template doesn’t produce 100 emails for me. Much like with a programming language, just learning the language doesn’t actually mean you know how to run a program. For example you might read in your first JavaScript tutorial that ” var x = 5 + 3 ;” is JavaScript, but you might not know exactly how you could run the code. Similarly with Mustache, you might learn about the language in a tutorial, but have no idea how to actually convert your templates to an output. Or to put it differently: How the heck can I create my 100 emails from 1 single Mustache template? You’ll need a …

Templating Engine

So once a syntax aka. templating language is defined, you need a tool that fills the placeholders in the template(s) with actual data and produces an output. This is called a templating engine:

To strech this point again, Mustache is not the templating engine. It’s the specification for how the template file must look like.

Or to come back to the example with programming languages. Mustache is the language. Not the execution environment. You can learn about the language in a book. But to actually use the language, you’ll need a computer and something that can interpret and run your code. The programming language JavaScript, for example can be ran and interpreted by Firefox, Chrome, Microsoft Edge and Safari, but they all use a different underlying engine to interpret and run it.

What’s the Templating Engine for Mustache?

Like for JavaScript there, isn’t just one thing that can compile Mustache. There’s a multitude of tools out there that can take a Mustache Template, some input data, and produce an output. The multitude of tools is actually overwhelming. There are tools that do their job well. And then there are obsolete ones that are badly maintained and don’t actually adhere to the Mustache specification. So which one should you choose from the multitude of options? The answer is: It depends on where and how you want to use Mustache. I will make some examples here, but I encourage you not to get lost in the actual links to those tools, but rather stick with the tutorial. When you leave now, you’ll be into the nitty gritty of how to use one tool without having the greater picture.

Example 1: You have a Java server and want to send out emails to users. So you use our Mustache Template from above for the emails and extend it a bit. So now you’d like to tell your Java code “hey, I’ve got a mustache template and 3 users that go by the name of [“Hans”, “Gertrude”, “Fritz”], can you send them a welcome mail”? Then your best bet would be to use the most popular Mustache Engine written in Java . Why? Because you have the data already in your Java code. The Mustache template file you can load through regular I/O:

String[] users = {"Hans","Gertrude","Fritz"};
for (String user : users) {
  String renderedEmail = mf.compile("emailTemplate.mustache", user);
  // ...then send email to user...

Example 2: You have a Python server and want to send an E-Mail. Pretty much the same as above, except that you’d use the most popular Mustache Engine written for Python

Example 3: You just want to print 100 personalized invitations to your birthday party, but have no idea about coding whatsoever (which is obvious, since coders don’t have 100 friends). Then unfortunately Mustache is not a good choice for you, since literally all engines for Mustache are connected to a programming language. There isn’t such a thing that takes an excel list of invitees plus a Mustache template and generates 100 pdfs out of it. At least I haven’t found something like that. Even though in principle it would work since it’s just a case of [Template + Input Data = Output] there just aren’t enough people that want to build their birthday cards that way…

Actually, this example 3 was really educational. We’ve learned that all available Mustache engines are connected to programming languages. Which ones are the available languages?

Here’s a screenshot from the official Mustache page answering that question:

Okay, that’s quite a lot of implementations. As I mentioned, some are good, some are bad. For example, the one listed for “node.js” is not maintained and buggy, while the one under “JavaScript” actually also supports node.js. So as you can see this list isn’t the be-all end-all. Maybe some of the listed links are outdated. Maybe some other ones are not included. At the same time, it’s still the best list that I’m aware of.

So now that we’ve covered the conceptual basics, we can dive deeper into the Mustache Templating language. But let’s first repeat what we’ve learned so far:

  • Mustache is a templating language, not a templating engine
  • There are tons of implementations for mustache engines, but be careful when choosing since not all are good.

Learning the Mustache Template Syntax

Now for the rest of the tutorial you have two choices. Either you select a templating engine from above and learn how it works. Then you can actually test the examples on your machine, which makes it a bit more tangible. But since Mustache is just a language, you might also be lying at a beach right now, slurping your drink, and reading the rest of the tutorial.

The Basics

We already had saw our very first template above, but let’s repeat it in a similar fashion because it was so beautiful:

Hello {{planet}}
  "planet": "Earth"

We choose JSON to describe our data here, but this would depend on the engine you’re using.

The template plus the data would produce the output:

Hello Earth

Now depending on your tool this output might be stored in a variable of your program or written to the filesystem. For example, the JavaScript mustache engine has two modes: Either it can be used as a command line tool to produce files, or it could be used to dynamically render html files.

Like what you're reading?    Get the latest updates first!

No spam. Just great engineering posts.


In Mustache, there are no if and else statements and also no loops. That’s why they call it logic-less templating. At least they’re not directly represented as keywords in the templating language. It is however possible to conditionally show or hide a block of text. And this is how:

Mustache Template:

  la di da


  "myCondition": false



Loops through Arrays / Lists

Loops are also not represented directly in the mustache language, but indirectly via the data and how the mustache specification specifies behaviour when the data is an array.


  "users": ["Hans", "Fritz", "Geraldine"]

Mustache Template:




If you want all names on the same line, you’ll have to write {{#users}}{{.}} {{/users}} on one line in the template (and put a space in there for legibility).

The elements could also be objects instead of strings, in which case access is given by just writing the property names into the brackets:


  "users": [{
    "name": "Hans"
  }, {
    "name": "Fritz"
  }, {
    "name": "Geraldine"

Mustache Template:




Using templates inside of templates: “Partials”

One Mustache template can load another template. The specification states that this should work with the syntax

I am a template and I'm loading
{{> template2 }}
and now I continue

This works fine as long as template2 doesn’t require template1, then you’ll get an infinite recursion which your templating engine can’t handle.

Passing Data to Partials

Passing data to partials is actually a bit tricky and one of the things that took me the longest to figure out. It works like this:

  {{> mybutton}}
  {{> mybutton}}

and in the data you’d specify

  "buttonOne": {
    "title": "Cancel"
  "buttonTwo": {
    "title": "Save"

In the partial there could be the code

<button class="my-cool-button">{{title}}</button>

The result would print

<button class="my-cool-button">Cancel</button>
<button class="my-cool-button">Save</button>


Comments begin with an exclamation point and are ignored. The following template:

<h1>Hello{{! ignore me }}.</h1>

Will render as follows:


Comments may contain newlines.

Summarized Learnings

Grasping the concepts at first isn’t easy because there are so many different implementations of Mustache and they’re often confused with the actual Mustache specification. Yet through a clear separation, the Mustache language can be learned independently, and then the skills can be used together with a carefully chosen engine (since not all of them are good or adhere to the official Mustache specs). Thanks for reading the tutorial, I hope you learned a lot, don’t forget to subscribe if you’re interested in Web Development and share the post with friends!


Templating Engine for HTML (special case AMP)

I’m usually working with Angular. But recently I started an AMP Project ( For those of you who don’t know AMP, it’s short for “Accelerated Mobile Pages” where you use only the “Official AMP JavaScript Libraries” but no JavaScript of your own and only prerendered HTML. The goal of this is to make webpages blazingly fast. Since a requirement is precompiled html I can’t use Angular as a templating engine since that dynamically compiles the html. Even though it has a static option with Angular Universal (don’t get me started on this…), the Angular Universal rendered page still includes JavaScript so it’s not possible to render an AMP compatible (=JavaScript-free) page with it.

So I’m kind of back in the “dark ages” where I’m stuck with templating engines such as Mustache, Handlebars, EJS or Pug (former Jade). Why I need a templating engine should be obvious: I don’t want to copy / paste code snippets all around and duplicate them like crazy. So I need a templating engine. Just which one’s right? So I set out to choose my templating engine for html. Which has been an absolute nightmare. Let’s review our options.


Mustache, we’ve all heard of it. Logic-less templating. So the entry point to the “mustache experience” is this one:

Since mustache is basically just a spec, it can be implemented in any language. That’s why you see so many languages there. I first clicked on the node.js link which leads to this godforsaken project I’ve used now for a quarter year:

The owner of the repo doesn’t seem to give two sh*ts about it. Last commit somewhen in 2016. Four unmerged pull requests One of them is mine. No response whatsover.

Then I tried the one when clicking on “JavaScript” in mustache: . Aha, that seems to be the right one with 11k github stars! Okay, I can’t concentrate because of that gif with the dude with the mustache. Is it just me, or is it really annoying to put that gif there? Maybe I’m just having a bad day. But then again, the command line tool also doesn’t seem to be ideal for me because I have to specify all used partials:

mustache -p path/to/partial1.mustache -p path/to/partial2.mustache dataView.json myTemplate.mustache

Can someone explain to me why this is necessary? The partials are already specified in the template! The first mustache compiler I mentioned didn’t require me to pass in all partials at compile time. I mean my template.mustache looks like this:

{{ > path/to/partial.html }}

Why do I have to pass this partial as a compiler option again? Especially if you have many views with many partials this becomes very tedious. And if the partial uses another partial? How am I supposed to track the usage of my partials? Unless … I don’t track it and just include all my partials everywhere. Then I’ll just have to register them ONCE in a single file, which is okay.

For those of you coming from Angular like myself, you’ll ask how can I pass data to partials?! You’d expect a syntax like

{{> mybutton title="Cancel" }}
{{> mybutton title="Save" }}

but this doesn’t exist. Instead you’ll set the variable inside the partial by the context you surround it with. An example:

  {{> mybutton}}
  {{> mybutton}}

and in the data.json you’d specify

  "buttonOne": {
    "title": "Cancel"
  "buttonTwo": {
    "title": "Save"

In the partial there could be the code

<button class="my-cool-button">{{title}}</button>

The result would print

<button class="my-cool-button">Cancel</button>
<button class="my-cool-button">Save</button>

It’s a bit of a change a first, but workable.

On a sidenote, there’s another small issue problem with the command line tool. The doc’s state:

The command line tool is basically a wrapper around Mustache.render so you get all the features.

But then their example is actually a demonstration that this doesn’t work:

var view = {
  title: "Joe",
  calc: function () {
    return 2 + 4;

var output = Mustache.render("{{title}} spends {{calc}}", view);

where the view object is what’s supposed to go to  dataView.json (see above) which is obviously not possible since a JSON can’t hold a function value…


Remember how I said my project can’t have any extra JavaScript libraries? Now, the precompilation of handlebars seems to be so stupid, that it still requires a JavaScript library on runtime:

<script src="/libs/handlebars.runtime.js"></script>

Duuuudes, come on, you’re killin me…


Since the name “Jade” was copyright infringement, they changed it to pug. In case you don’t know it, it’s a html templating engine, that doesn’t look like html at all. It’s whitespace sensitive and you’ll have to learn a new language. For me, I don’t like it that they deviated so much from the standard html for a couple of reasons:

  • No copy / pasting of code snippets from nowhere (one might say that’s a actually a good thing…)
  • You’ll have to learn a “new language”
  • All team members will have to learn a new language.
  • Larger chance of getting out of fashion than when sticking with something similar to html like mustache (yet small chance, since it’s been here for a while and has too many users to be completely abandoned)
  • I don’t really find the whitespace based syntax better
  • My IDE doesn’t do it’s job as well

So all in all I have very little incentive to use pug, I just see a point of deviating that much from HTML.


EJS has horribly little documentation:

It doesn’t seem to be what I’m looking for: A command line tool to precompile some html templates. Rather you put your html as strings into into JavaScript files, which neither me nor my IDE like very much. As for storing html snippets in a separate file, then requiring them through JS, then paring to a string, and then storing that string again in a file, that’s just too complicated.


Mustache is the winner. Clean syntax that integrates well with html. Large community supporting it. Been here for a while. Not going anywhere anytime soon. Once you get it, you got it. However, it took me a while getting it, since the approach to handling templates and data seems weird when you come from Angular. This article on “passing variables to partials” has been a bit of an eyeopener for me, so thanks Pawel!

What’s up with Angular Universal?

I was excited when I heard Angular 2 would support server-side rendering since SEO was a huge PITA (pain in the ass) with Angular 1. It was said that server-side rendering would be possible through Angular Universal.

Yet, this Angular Universal thing, what’s up with that? Seems to have a really small team on that and there doesn’t really seem to be any documentation. Well at least there’s a getting started repo, so I thought. However, this repo ( doesn’t even have a readme…

Also, when you check out some of those files, they don’t seem very professional. For example:

First line of code: //hacky express wrapper thingy.


Then of course, there’s also some confusion as to where to go and look for resources. You’ll find quite a few.

When you just google “Angular Universal” (as of September 2017) you’ll get to However, this page is a bit out of date.

Just have a look at the date:

Or the actual site:

Angular 2 is now Angular 4 and --ambient is also not a thing anymore. So the only purpose of is to confuse people with outdated material and to link to github (

Github, even though being the best resource on Angular Universal, isn’t really documented either. Meh.

Well, I guess switching to React isn’t such a bad idea after all?

PS: I wrote this article in May 2017, I’ve checked again in September and now, even though the docs are still a mess, there’s a working demo here: