diff --git a/assets/_/styles.scss b/assets/_/styles.scss
index e1783f9..9c58fcd 100644
--- a/assets/_/styles.scss
+++ b/assets/_/styles.scss
@@ -178,6 +178,12 @@ pre {
     overflow: auto
 }
 
+/* overriding the one below: if code is highlighted,
+   size should remain the same.
+*/
+div.highlight pre code {
+    font-size: 1em;
+}
 pre code {
     border: 0;
     background: none;
diff --git a/content/log/2022/uber-mock-interview-retrospective.md b/content/log/2022/uber-mock-interview-retrospective.md
new file mode 100644
index 0000000..c2025fe
--- /dev/null
+++ b/content/log/2022/uber-mock-interview-retrospective.md
@@ -0,0 +1,287 @@
+---
+title: "Uber Mock Interview Retrospective"
+date: 2022-07-01T12:55:00+03:00
+slug: uber-mock-interview-retrospective
+---
+
+Like mentioned in [the previous post]({{< ref "log/2022/big-tech-hiring" >}}),
+I did a public mock coding interview. A reminder what that was:
+
+- The goal was to explain how some bits of Uber's tech recruiting works.
+- The [meetup page][meetup-page] had 602 attendees as of writing. We expected
+  quite a few participants in the event.
+
+The mock interview consisted of:
+- Introduction by Uber's EMEA recruiter Courtney Cox.
+- Myself doing a coding challenge with a 50 minute cap:
+  - I did not know the exercise upfront.
+  - Although my job did not depend on it, the ticking timer and people looking
+    at my work (~260) made it quite stressful.
+  - I did not complete the exercise. According to my interviewee, I failed the
+    "phone screen". The good part is that I still get to keep my job. :)
+- Half-hour QA session.
+
+## TLDR: Highlights
+
+- Lots of fun for everyone: myself, the interviewer and the spectators.
+- Folks seemed to be engaged: the chat room was active throughout, and we had
+  more questions than time to answer them.
+- Even though I have been coding Zig for the last few months, I felt like I had
+  a strong enough grip on it; the algorithm was the one that tripped me.
+
+## TLDR: Lowlights
+
+Most importantly, I did not complete the exercise. Worse, I did not even come
+up with the correct algorithm, therefore the interview was an obvious failure.
+I can re-apply in 6 months though!
+
+Biggest mistake? **I did the same mistake that interviewees do all the time:
+start coding without knowing the full algorithm.** This is a recipe for
+failure. It is always, always better to spend 10-15 minutes with hands off the
+keyboard and come up with the solution, and only then start coding.
+
+On the same day I figured out the solution and implemented it next morning. You
+can find it below. If you want to show this off in your favorite programming
+language, read below in the [challenge](#optional-challenge-for-you) section.
+
+## The Exercise and Solution
+
+I have been coding in Zig since last February (so ~5 months now). [Loris
+Cro][loris] keeps telling the me and The Internet that Zig is not suitable for
+coding challenges. Well, after a couple of months of working with him, I can
+finally say he is wrong! Even though my colleagues tell me Zig was tripping me
+(e.g. memory leaks in the unit tests, for which I had to add `defer
+hash_map.deinit()`), I think this was due to lack of experience in a coding
+challenge setting. Next time the first thing I will do is construct an area and
+be done with memory management.
+
+Exercise was taken from [Cracking the coding interview][cracking]:
+
+```
+// Each year, the government releases a list of the 10000 most common baby
+// names and their frequencies (the number of babies with that name). The only
+// problem with this is that some names have multiple spellings. For example,
+// "John" and "Jon" are essentially the same name but would be listed
+// separately in the list. Given two lists, one of names/frequencies and the
+// other of pairs of equivalent names, write an algorithm to print a new list
+// of the true frequency of each name. Note that if John and Jon are synonyms,
+// and Jon and Johnny are synonyms, then John and Johnny are synonyms. (It is
+// both transitive and symmetric.) In the final list, any name can be used as
+// the "real" name.
+//
+// Example:
+// Names: John (15), Jon (12), Chris (13), Kris (4), Christopher (19)
+// Synonyms: (Jon, John), (John, Johnny), (Chris, Kris), (Chris, Christopher)
+// Output: John (27), Kris (36)
+```
+
+### My solution
+
+Timeline:
+- 50 minutes during the interview. I almost did not use any of it except for
+  small parsing bits and the unit test.
+- 30 minutes after cycling home right after the meetup: thinking about the
+  problem. At this point I realized this problem reduces to finding
+  disconnected graphs.
+- 2 hours 15 minutes: coding. The result of that is below.
+
+{{< highlight zig "linenos=table" >}}
+const std = @import("std");
+const mem = std.mem;
+const fmt = std.fmt;
+const Order = std.math.Order;
+const Allocator = std.mem.Allocator;
+const ArrayList = std.ArrayList;
+const ArrayListUnmanaged = std.ArrayListUnmanaged;
+const StringHashMap = std.StringHashMap;
+const PriorityQueue = std.PriorityQueue;
+
+// for priority queue
+fn lessThan(_: void, a: u32, b: u32) Order {
+    return std.math.order(a, b);
+}
+
+pub fn solution(
+    allocator: Allocator,
+    names: []const u8,
+    synonyms: []const u8,
+) error{OutOfMemory}![]const u8 {
+    var arena1 = std.heap.ArenaAllocator.init(allocator);
+    defer arena1.deinit();
+    var arena = arena1.allocator();
+
+    var name2id = StringHashMap(u32).init(arena);
+    var pairs = ArrayList([2]u32).init(arena);
+
+    // populate name2id and pairs
+    const total_members = blk: {
+        var it = mem.tokenize(u8, synonyms, ", ()");
+        var idx: u32 = 0;
+        while (true) {
+            const left = it.next() orelse break;
+            const right = it.next().?;
+            var pair: [2]u32 = undefined;
+            var i: u2 = 0;
+            for (&[_][]const u8{ left, right }) |val| {
+                const result = try name2id.getOrPut(val);
+                if (!result.found_existing) {
+                    result.value_ptr.* = idx;
+                    pair[i] = idx;
+                    idx += 1;
+                } else pair[i] = result.value_ptr.*;
+
+                i += 1;
+            }
+            try pairs.append(pair);
+        }
+
+        // now add all "lone" names that do not have aliases
+        var it2 = mem.tokenize(u8, names, "(), 0123456789");
+        while (it2.next()) |name| {
+            const result = try name2id.getOrPut(name);
+            if (!result.found_existing) {
+                result.value_ptr.* = idx;
+                idx += 1;
+            }
+        }
+
+        break :blk idx;
+    };
+
+    // create id2name for printing the results
+    var id2name = try arena.alloc([]const u8, total_members);
+    {
+        var it = name2id.iterator();
+        while (it.next()) |val|
+            id2name[val.value_ptr.*] = val.key_ptr.*;
+    }
+
+    var graph = try arena.alloc(ArrayListUnmanaged(u32), total_members);
+    mem.set(ArrayListUnmanaged(u32), graph, ArrayListUnmanaged(u32){});
+
+    // populate graph
+    for (pairs.items) |pair| {
+        try graph[pair[0]].append(arena, pair[1]);
+        try graph[pair[1]].append(arena, pair[0]);
+    }
+
+    // navigate through graph. This is DFS:
+    // - "visited" is a list of user ids that we should not go into.
+    // - "unvisited" is a queue of user ids that we need to visit. This is
+    //   the driver of the loop: work until this is non-empty.
+    var visited = try arena.alloc(bool, total_members);
+    mem.set(bool, visited, false);
+
+    // everyone is unvisited now
+    var unvisited = PriorityQueue(u32, void, lessThan).init(arena, {});
+    try unvisited.ensureTotalCapacity(total_members);
+    for (id2name) |_, i|
+        try unvisited.add(@intCast(u32, i));
+
+    // id2synonym is mapping from userid to synonym_id. It just so conveniently
+    // happens that the synonym_id points to a user id.
+    var id2synonym = try arena.alloc(u32, total_members);
+
+    // traverse the graph and populate id2synonym
+    {
+        var synonym_id: u32 = 0;
+        // scratch is our DFS temporary storage: while traversing the member
+        // list, which ones to go to when we're done with the current one?
+        var scratch = PriorityQueue(u32, void, lessThan).init(arena, {});
+
+        while (unvisited.removeOrNull()) |i| : (synonym_id += 1) {
+            if (visited[i]) continue;
+
+            try scratch.add(i);
+            while (scratch.removeOrNull()) |j| {
+                visited[j] = true;
+                id2synonym[j] = synonym_id;
+                for (graph[j].items) |k|
+                    if (!visited[k])
+                        try scratch.add(k);
+            }
+        }
+    }
+
+    var id2count = try arena.alloc(u32, total_members);
+    mem.set(u32, id2count, 0);
+    // calculate id2count from names and id2synonym
+    {
+        var it = mem.tokenize(u8, names, ", ()");
+        while (true) {
+            const name = it.next() orelse break;
+            const id = name2id.get(name).?;
+            const count = fmt.parseInt(u32, it.next().?, 10) catch unreachable;
+            id2count[id2synonym[id]] += count;
+        }
+    }
+
+    var result = ArrayList(u8).init(allocator);
+    const wr = result.writer();
+    for (id2count) |count, id| {
+        if (count == 0) continue;
+        if (id != 0) result.appendSlice(", ") catch unreachable;
+        wr.print("{s} ({d})", .{ id2name[id], count }) catch unreachable;
+    }
+    return result.toOwnedSlice();
+}
+
+const tests = [_]struct {
+    names: []const u8,
+    synonyms: []const u8,
+    want: []const u8,
+}{
+    .{
+        .names = "John (15), Jon (12), Chris (13), Kris (4), Christopher (19), Žvangalas (10)",
+        .synonyms = "(Jon, John), (John, Johnny), (Chris, Kris), (Chris, Christopher)",
+        .want = "Jon (27), Chris (36), Žvangalas (10)",
+    },
+    .{
+        .names = "John (15), Jon (12), Chris (13), Kris (4), Christopher (19)",
+        .synonyms = "(Jon, John), (John, Johnny), (Chris, Kris), (Chris, Christopher)",
+        .want = "Jon (27), Chris (36)",
+    },
+    .{
+        .names = "John (15), Jon (12), Johnny (5), Johnn (4), Johnathan (3)",
+        .synonyms = "(Jon, John), (Johnn, Johnny), (Johnathan, Jon), (John, Johnny)",
+        .want = "Jon (39)",
+    },
+};
+
+const testing = std.testing;
+test "example" {
+    for (tests) |tt| {
+        const got = try solution(testing.allocator, tt.names, tt.synonyms);
+        defer testing.allocator.free(got);
+        try testing.expectEqualStrings(tt.want, got);
+    }
+}
+{{< / highlight >}}
+
+[Jakub Konka][jakub] was watching the interview too! His comment to the
+solution above is:
+
+- Your solution looks good to me and I think I'd be oscillating roughly around
+  the same solution too.
+- You've used arena in an interesting way to init string sets: I think I'd use
+  an unmanaged version and initialize on first use.
+- But it's fine either way.
+
+## Optional: challenge for you
+
+Inclined to show off your solution in Zig or your favorite programming
+language? Post it to the comment in [meetup page][meetup-page] (preferably use
+a [public pastebin][pastebin] to keep the comment size reasonable), and I will
+paste my favorite ones here with your name. Please include the time it took you
+to code it. The main criteria is, of course, lines of code. :)
+
+## Thanks
+
+Many thanks to Courtney Cox, Mantas Mikšys, Brigita Žemgulytė and Ignas
+Kaziukėnas for making this meetup happen. I would do it again.
+
+[loris]: https://kristoff.it/
+[meetup-page]: https://www.meetup.com/uber-engineering-events-vilnius/events/286542203/
+[jakub]: https://www.jakubkonka.com/
+[pastebin]: https://paste.mozilla.org/
+[cracking]: https://www.crackingthecodinginterview.com/