Canonical · Software Engineer
Updated · 2026-10-02

Canonical Software Engineer
Interview Guide

THE 60-SECOND BRIEF

Canonical is the company behind Ubuntu. Candidates describe its software as running on public cloud instances, enterprise servers, IoT devices and developer workstations. The engineering areas they list for the role include the Linux kernel, Kubernetes and other container tooling, system packaging (Debian packages and Snaps) and cloud-init. A Software Engineer there writes Python, Go, Rust or C/C++, builds test suites, takes part in code review, and works with upstream open-source communities. The reported work also covers CI, packaging and deployment across platforms, tracking down low-level bugs, performance tuning and getting security fixes into the Ubuntu ecosystem. The team is described as fully remote and spread across the world, with most communication in writing through documentation, email and issue trackers.

This guide is for engineers preparing for a Software Engineer loop at Canonical. It covers the four reported stages, the written essay questions, the reported Linux and systems questions, language-semantics questions (Python, Go, Rust), the four system design questions candidates report, and a story bank for the open-source and leadership questions. It is written for candidates whose target team uses Python, Go, Rust or C/C++, and it pairs each reported question with the technique, trade-offs and edge cases worth rehearsing.

Candidates report 4 rounds over roughly 3-5 weeks: an Asynchronous Assessment, Technical Coding Challenges, Domain-Specific Interviews and an Executive Leadership Review. Some candidates say the whole process can stretch to months.

PythonWritten interviews / essaysLinux / Unix

46 min read

Browse Software Engineer questions

See the practice prompts

8Candidate experiences ↗Read their reports
24Practice promptsAcross five skill areas

The reported loop front-loads written work. Before any live conversation, candidates describe a long written questionnaire (academic background, proudest project, leadership, open-source experience) and a timed cognitive test called the Thomas GIA that covers perceptual speed, number speed and accuracy, word meaning and spatial visualization. Preparation for this stage is different from preparation for a coding screen: you write real answers in advance, keep specific stories ready, proofread them, and practise speed-with-accuracy puzzles.

The live technical material reported for this role leans toward fundamentals rather than frameworks. The Linux questions ask for mechanisms: the boot path from firmware to user space, processes versus threads and kernel scheduling, how a shell builds a pipe, virtual memory and the OOM killer, and a safe kernel upgrade with a way back. Candidates also mention fork and exec, troubleshooting a server with high load average but low CPU use, and tools such as strace, lsof and htop. Study these until you can explain the system calls and kernel structures involved, not just the vocabulary.

Language questions are split across Python, Go and Rust depending on the team: decorators, goroutines against OS threads and the Go scheduler, Rust ownership, borrowing and lifetimes, an ISBN check-digit function, and the cost of hash maps and slices in your main language. Reported coding scenarios include a concurrent LRU cache in Go and an asynchronous Python log parser that emits a JSON report. Prepare in the language you will actually be assessed in, and be able to state complexity and memory behaviour precisely.

The four design questions all sit close to infrastructure: a highly available package-manager metadata service, a lightweight monitoring agent for thousands of resource-constrained IoT devices, an API gateway with rate limiting, authentication and dynamic routing, and deployment automation for microservices across hybrid clouds. They are not four variations of one problem, so practise each with its own constraints, and keep written answers structured, since written communication is a theme across the reported stages.

01

Asynchronous Assessment

reported

Candidates describe this stage as written assessments plus cognitive testing, completed asynchronously before any live conversation. The written part is a multi-page questionnaire on academic background, career milestones, open-source history and motivation. The cognitive part is the Thomas GIA, which candidates describe as covering perceptual speed, number speed and accuracy, word meaning and spatial visualization such as spotting mirrored letters. Approach it as two separate jobs: write specific, proofread answers built from real projects, then train for speed without sacrificing accuracy.

What to demonstrate

  • Written communication: clear, structured, precise answers about your own work and background
  • Self-reflection and the ability to give concrete detail about projects, contributions and trade-offs
  • Speed and accuracy on timed cognitive tasks (perceptual, numerical, verbal, spatial)

How to prepare

  • Write full answers to the essay-style questions about why Canonical, your proudest technical achievement, a leadership role, and your open-source experience, using specific projects, decisions and outcomes, then proofread them twice
  • Prepare honest answers about your mathematics and physics background, including what you took and how you performed, before you are asked
  • Run timed practice in each GIA area (number comparison, visual difference spotting, synonyms and antonyms, mirrored shapes) and favour a high accuracy rate over finishing everything
Canonical Software Engineer candidate reports ↗
02

Technical Coding Challenges

reported

Candidates report technical coding challenges at this point, with emphasis on classic data structure and algorithm problems, idiomatic and readable code, and the runtime behaviour of the language you choose. Scenarios candidates mention include a thread-safe LRU cache in Go and an asynchronous Python log parser that extracts error metrics into a JSON report. Pick one primary language among Python, Go and Rust and prepare in it, since the questions about complexity and memory footprint are framed around your primary language.

What to demonstrate

  • Working, idiomatic code in the language you chose
  • Choice of data structures with correct time and space complexity
  • Handling of edge cases and errors, for example in the ISBN check-digit function

How to prepare

  • Implement an LRU cache with a hash map plus doubly linked list, then make it safe for concurrent use and reason about lock granularity and contention
  • Write a log parser that reads a large file lazily, aggregates error counts and emits JSON, and decide where asynchronous I/O helps and where it does not
  • Write the ISBN check-digit validator for both ISBN-10 and ISBN-13 and list your edge cases (hyphens, a trailing X, wrong length, non-digit characters) before you code
  • Prepare a short account of hash-map and slice costs in your language: average and worst-case lookup, amortized growth, resize behaviour and extra memory per entry
Canonical Software Engineer candidate reports ↗
03

Domain-Specific Interviews

reported

Candidates describe live interviews on specific technical domains relevant to the role. The material reported for this stage family is Linux and systems fundamentals (process management, signals, boot sequence, networking and I/O, cgroups and namespaces, profiling tools) and language semantics in Python, Go or Rust. Candidates advise being ready for specific commands, flags, system calls and kernel behaviour rather than high-level architecture talk. Practise explaining mechanisms aloud in order, from cause to effect.

What to demonstrate

  • Depth of Linux internals: boot, processes and threads, pipes, virtual memory, kernel upgrades
  • Understanding of runtime and memory behaviour in Python, Go or Rust
  • Troubleshooting method on a misbehaving system, for example high load average with low CPU utilization

How to prepare

  • Rehearse the boot sequence step by step (firmware, bootloader, kernel and initramfs, systemd, targets) and say what each stage hands to the next
  • Explain fork and exec, copy-on-write, zombies and signals, then contrast threads and processes in terms of shared address space and scheduler entities
  • Build a debugging sequence for high load with low CPU: check for tasks in uninterruptible I/O wait, look at disk and network, then use strace, lsof and per-process views
  • Practise a safe kernel upgrade plan: staged rollout, keeping the previous kernel installed and selectable in the bootloader, a reboot window, health checks and a rollback trigger
Canonical Software Engineer candidate reports ↗
04

Executive Leadership Review

reported

Candidates describe a final review by executive leadership to assess overall fit with the organization. Very little detail is reported about its format, so prepare for a conversation rather than a technical test: your essay stories, your open-source experience, your reasons for wanting a fully remote, written-first working style, and your practical questions about the contracting and travel arrangements candidates mention.

What to demonstrate

  • Overall fit with the organization, as the stage is described
  • Consistency between what you wrote earlier and what you say live
  • Your motivation and your account of working autonomously in a remote team

How to prepare

  • Re-read your written answers and make sure every claim can be expanded into a two-minute spoken story with a decision, a trade-off and a result
  • Prepare a concise answer on your open-source use and contributions, and one on how you work without daily oversight
  • Write down your questions about contract status, tax implications and in-person sprint travel so you can raise them calmly
Canonical Software Engineer candidate reports ↗

8 candidate reports. Individual accounts describe a particular role and hiring cycle.

Software Engineer

Canonical Software Engineer interview with multiple assessments and late-stage rounds

Online Assessment → Technical Screen → HR Screen → OtherOutcome: rejected

I interviewed for the Software Engineer role through a drawn-out but structured process. I completed several assessments, followed by interviews covering coding, systems concepts, and behavioral collaboration. I started with a written-style screen, then completed logic and personality testing. The technical stages included long interview sessions, another technical segment later in the process, a…

Read full experience
Software Engineer

Canonical Software Engineer interview: Python, Docker, Linux, and Kubernetes rounds

Technical Screen

I interviewed for the Software Engineer role through a long, executive-heavy process with multiple technical rounds. The questions covered Python, Docker, Linux, and Kubernetes, and I spoke with senior leaders and team leads. I didn't receive an offer. After all the stages and conversations, the final decision was described as a skills-set mismatch. Location: India. Overall feedback: Negative. Of…

Read full experience
Software Engineer

Canonical Software Engineer interview with questionnaire and Python assessment

Online Assessment

I interviewed for the Software Engineer role through a multi-assessment process that started with a long questionnaire and two tests, before any deeper technical discussion. I answered around 30 questionnaire questions. The first assessment was a non-standard test made up of different types of tasks. The second assessment focused on Python. I didn’t receive an offer. Early filtering leaned heavil…

Read full experience
Software Engineer

Canonical Software Engineer interview spanning React, C++, Linux, and full-stack JavaScript

Technical Screen

I interviewed for a Software Engineer role through a multi-interview, all-technical process. It moved from front-end topics into broader full-stack engineering, including Linux and open source. In the front-end interview, I was asked about React and general front-end topics. The systems and programming interview shifted to C++ and Linux. Another round covered full-stack development fundamentals i…

Read full experience
Software Engineer

Canonical Software Engineer technical rounds on Linux and open source

Technical Screen

I interviewed for the Software Engineer role and went through a two-track technical process in the middle of a longer interview sequence. One track focused on architecture and engineering depth. The other covered Linux, open-source topics, and platform fundamentals. In the software architecture and engineering round, I was asked about architecture qualities, engineering methodologies, and how eng…

Read full experience

PracHub editorial advice for the preparation topics above.

01

Submitting generic or typo-ridden written answers to the essay-style questions

Write each answer around one real project with a decision, a trade-off and an outcome. Candidates report that generic, machine-sounding answers and careless errors hurt, so keep your own voice and proofread after a break.

02

Racing through the Thomas GIA and losing points to simple mistakes

Practise each area (perceptual speed, number speed and accuracy, word meaning, spatial visualization) under a timer, and set a personal rule to keep accuracy high even if a few items go unanswered.

03

Reciting the Linux boot process as a list of names (BIOS, GRUB, kernel, systemd) with no mechanism

For each stage say what it loads and what it hands over: firmware initialises hardware and finds the bootloader, the bootloader loads kernel and initramfs, the kernel mounts the real root and starts PID 1, and systemd brings up targets. Be ready to name the commands and logs you would check at each stage.

04

Describing goroutines, decorators or Rust ownership with slogans instead of runtime behaviour

Prepare precise statements: goroutines are scheduled by the Go runtime onto a smaller set of OS threads; a decorator is a callable that replaces a function at definition time; Rust ownership gives each value one owner, with borrows checked at compile time. Add one short code example for each.

05

Answering the IoT monitoring agent or package metadata design like a generic web service

State the constraints first: memory and CPU budget and flaky connectivity for the agent; read-heavy mirrored metadata with stale-read tolerance for the package service. Then choose protocols, caching, batching and failure behaviour that follow from those constraints.

Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.

21 technical prompts0 include a worked solution

What is the difference between a process and a thread in Linux, and how does the kernel schedule them?

medium
Linux & Systems Engineering

What is the difference between a process and a thread in Linux, and how does the kernel schedule them?

Approach
  1. In Linux both are tasks (task_struct). fork() creates a child with its own address space using copy-on-write pages. A thread is created with clone() flags such as CLONE_VM, CLONE_FILES, CLONE_FS, CLONE_SIGHAND and CLONE_THREAD, so it shares memory, file descriptors and signal handlers with its group.
  2. Per thread: its own stack, registers, TID, signal mask and scheduling state. Threads in a group share one PID (the TGID), which is why ps shows one process and ps -L or /proc//task shows its threads.
  3. The scheduler schedules each thread independently. The normal class was CFS (a red-black tree ordered by weighted vruntime, with nice values setting weights), replaced by EEVDF in kernel 6.6. Real-time classes SCHED_FIFO and SCHED_RR, and SCHED_DEADLINE, take priority over normal tasks.
  4. Trade-off: switching between threads of one process skips the address-space switch and is cheaper, and sharing memory avoids IPC. The cost is that shared state needs locking, and a fault such as SIGSEGV in one thread kills the whole process. Separate processes isolate crashes.
  5. Edge case to raise yourself: fork() in a multithreaded program copies only the calling thread, so locks held by other threads stay locked in the child. Call only async-signal-safe functions before exec.
Follow-up
  • What happens to a mutex held by another thread when a multithreaded process calls fork()?
  • How would you pin a latency-sensitive thread to a CPU, and what does it cost the rest of the system?
  • How do nice values change the CPU share two busy tasks get?

How does virtual memory management work in Linux, and what is the role of the Out-Of-Memory (OOM) killer?

medium
Linux & Systems Engineering

How does virtual memory management work in Linux, and what is the role of the Out-Of-Memory (OOM) killer?

Approach
  1. Each process gets its own virtual address space. Page tables map virtual pages (usually 4 KiB, or huge pages) to physical frames, the MMU translates and the TLB caches translations. Accessing an unmapped page triggers a page fault: minor if the page is already in memory, major if it needs disk I/O.
  2. Allocation is lazy: malloc and mmap reserve address space, and physical pages arrive on first touch. fork shares pages copy-on-write. That is why VSZ is far larger than RSS, and why the kernel can overcommit (vm.overcommit_memory: 0 heuristic, 1 always, 2 strict commit limit).
  3. Under pressure, kswapd and direct reclaim drop clean page cache, write back dirty pages and swap anonymous memory. When reclaim cannot free enough, the OOM killer picks the task with the highest oom_score (mostly its RSS, swap and page-table usage, shifted by oom_score_adj from -1000 to 1000) and sends SIGKILL.
  4. In containers a cgroup v2 memory.max limit triggers an OOM kill inside that cgroup only. Diagnose with dmesg or journalctl -k ('Out of memory: Killed process'), memory.events and /proc//status. Userspace killers such as systemd-oomd act earlier using pressure-stall information (PSI).
  5. Trade-off to state: setting oom_score_adj to -1000 protects a critical process but can shift kills onto the services it depends on. Strict overcommit avoids surprise kills but makes allocations fail sooner.
Follow-up
  • What is the difference between VSZ and RSS, and which would you alert on?
  • A container is OOM-killed while the host has free memory. Why?
  • How would you protect a database from the OOM killer, and what is the risk?

What are Python decorators, how do they work under the hood, and when would you use them?

medium
Programming & Language Semantics

What are Python decorators, how do they work under the hood, and when would you use them?

Approach
  1. @dec above def f is shorthand for f = dec(f), run once at definition time. Functions are first-class objects, so a decorator is any callable that takes a function and returns a replacement, usually a closure wrapping the original.
  2. Write the wrapper as def wrapper(*args, **kwargs) so it passes everything through, and apply functools.wraps(f). That copies name, qualname, doc and module, updates dict and sets wrapped, so introspection, logging and tests still see the original.
  3. A decorator with arguments is a factory: @retry(times=3) calls retry(times=3) first, which returns the actual decorator, so there are three nested levels. When stacked, the decorator nearest def is applied first, and the outermost wrapper runs first at call time.
  4. Name real uses: functools.lru_cache and functools.cache, property, staticmethod and classmethod, plus retries, timing, authorisation checks and registering handlers in a dict. Classes can also be decorators by defining call, and class decorators receive a class.
  5. Edge cases: decorating an async def needs an async wrapper that awaits the call. State kept in the closure is shared across all calls and threads, so it needs a lock. lru_cache needs hashable arguments and holds references to them.
Follow-up
  • Write a retry decorator that takes the attempt count and exceptions to catch, with exponential backoff.
  • Why does lru_cache fail on a list argument, and what memory risk does it carry on a method?
  • How would you decorate a function so it works for both sync and async callers?

Explain the difference between goroutines and OS threads in Go. How does the Go runtime schedule them?

medium
Programming & Language Semantics

Explain the difference between goroutines and OS threads in Go. How does the Go runtime schedule them?

Approach
  1. Goroutines are user-space tasks managed by the Go runtime. They start with a small stack (a few KiB) that grows by copying, while an OS thread gets a fixed stack reserved by the OS (often 8 MiB of virtual memory on Linux). Creating and switching goroutines costs far less, so many thousands can run.
  2. Scheduler model (G-M-P): G is a goroutine, M an OS thread, P a logical processor holding a local run queue. GOMAXPROCS sets the number of Ps, and so how many goroutines run Go code in parallel. There is also a global run queue, and idle Ps steal work from others.
  3. Blocking: in a blocking syscall, the M releases its P so another M can keep running that P's goroutines. Network I/O goes through the netpoller (epoll on Linux), so the goroutine parks and no thread is tied up. Channel and mutex waits park the goroutine, not the thread.
  4. Preemption: since Go 1.14 the runtime preempts long-running goroutines asynchronously using signals. Before that, a tight loop with no function calls could starve others.
  5. Trade-offs: goroutines make blocking-style code cheap, but they leak if a goroutine waits forever on a channel. Use context cancellation and watch runtime.NumGoroutine or pprof's goroutine profile. Use runtime.LockOSThread when a C library or thread-local OS state needs a fixed thread.
Follow-up
  • How would you find and fix a goroutine leak in a long-running service?
  • What should GOMAXPROCS be in a container with a CPU quota, and why does it matter?
  • Why can a cgo call or a blocking syscall increase the number of OS threads?

How does memory safety work in Rust without a garbage collector? Explain the concepts of ownership, borrowing,

medium
Programming & Language Semantics

How does memory safety work in Rust without a garbage collector? Explain the concepts of ownership, borrowing, and lifetimes.

Approach
  1. Ownership: every value has exactly one owner, and the value is dropped (Drop runs, memory is freed) when the owner goes out of scope. Assigning or passing a non-Copy value moves it, so the old binding cannot be used, which rules out double frees.
  2. Borrowing: you may hold any number of shared references (&T) or exactly one mutable reference (&mut T), never both at once. The borrow checker enforces this at compile time, which prevents data races, iterator invalidation and mutating data someone else is reading.
  3. Lifetimes: the compiler proves no reference outlives the value it points to, which rules out dangling pointers and use-after-free. Annotations like fn longest<'a>(x: &'a str, y: &'a str) -> &'a str describe how input and output lifetimes relate; they do not extend how long anything lives. Elision rules cover common cases.
  4. Escape hatches and their cost: Box for heap ownership, Rc or Arc for shared ownership through reference counting (a cycle leaks unless one side uses Weak), RefCell for borrow checks moved to runtime (a violation panics), Mutex or RwLock across threads, and unsafe blocks where the programmer upholds the rules. Send and Sync mark which types may cross threads.
  5. Be precise about the guarantee: safe Rust prevents memory-unsafety and data races, not leaks (mem::forget and Rc cycles are safe) and not deadlocks or logic races.
Follow-up
  • Why won't the compiler let you push to a Vec while holding a reference to one of its elements?
  • When would you choose Rc<RefCell> over restructuring ownership, and what does it cost?
  • What is the difference between Rc and Arc, and why is Rc not Send?

Write a function to validate an ISBN check digit and explain your approach to error handling and edge cases.

medium
Programming & Language Semantics

Write a function to validate an ISBN check digit and explain your approach to error handling and edge cases.

Approach
  1. Normalise first: strip hyphens and spaces, then require length 10 or 13. Check characters explicitly against '0'-'9'. In Python, str.isdigit() also accepts characters like '²', which int() then rejects. Allow 'X' (value 10) only as the last character of an ISBN-10, and decide whether lowercase x counts.
  2. ISBN-10: weight the digits 10 down to 1 and accept when the sum mod 11 is 0. Example: 0-306-40615-2 gives 132 = 12 x 11. ISBN-13: weights alternate 1,3,1,3...; accept when the sum mod 10 is 0, equivalently check digit = (10 - sum of the first 12 weighted mod 10) mod 10. Example: 978-0-306-40615-7.
  3. Error handling: separate malformed input (wrong length, bad character, X in the wrong place, None or non-string) from a well-formed number with a wrong check digit. I would return a bool from is_valid and raise ValueError with a reason from a parse function, or return a result type, and say why.
  4. Complexity is O(n) time and O(1) extra space. Test with known-valid numbers, each single-digit change, swapped neighbours, empty string, all hyphens, an X in the middle, and 13-digit strings that pass the checksum but do not start with 978 or 979 if you enforce the prefix.
  5. Explain what each scheme catches: mod 11 with weights 10..1 detects every single-digit error and every adjacent transposition. The 1-3 weighting of ISBN-13 detects every single-digit error but misses adjacent transpositions of digits that differ by 5.
Follow-up
  • Extend the function to convert a valid ISBN-10 into its ISBN-13 form.
  • Why does ISBN-13 miss some adjacent transpositions that ISBN-10 catches?
  • How would you validate a large file of ISBNs and report which lines failed and why?

Explain the computational complexity and memory footprint of standard data structures like hash maps and slice

medium
Programming & Language Semantics

Explain the computational complexity and memory footprint of standard data structures like hash maps and slices in your primary language.

Approach
  1. Pick your primary language and give average and worst case. Python dict: open addressing, average O(1) get, set and delete, O(n) worst case under heavy collisions, resize when about two-thirds full. Since 3.7 insertion order is guaranteed, thanks to the compact layout (an index array plus a dense entries array).
  2. Python list: a dynamic array of pointers. Index and append are O(1) (append amortised, through over-allocation), insert or pop at the front and the in operator are O(n). Memory: 8 bytes per slot on 64-bit plus each boxed object (a small int is 28 bytes), so array, bytes or NumPy are far denser for numbers.
  3. Go slice: a 24-byte header (pointer, len, cap) over a backing array. append is amortised O(1) and reallocates when cap is exceeded (doubling for small slices, a smaller factor for large ones). A subslice shares the backing array, so a small slice can keep a huge array alive, and appending through two slices can overwrite each other's data.
  4. Go map: a hash table with average O(1) operations and randomised iteration order. It is unsafe for concurrent writes (the runtime aborts with 'concurrent map writes'), so use a mutex or sync.Map. It does not shrink after deletes. Go 1.24 moved the implementation to Swiss tables.
  5. Show you can act on it: pre-size with make([]T, 0, n) or make(map[K]V, n), use a set for membership tests, use collections.deque for queue operations, and choose a sorted slice with binary search when memory matters more than O(1) lookup.
Follow-up
  • Why can a small subslice in Go keep hundreds of megabytes alive, and how do you fix it?
  • When would a sorted array with binary search beat a hash map in practice?
  • What makes a hash map degrade to O(n), and how do languages defend against hash-flooding inputs?

The plan follows the reported order: written assessment and cognitive speed first, then Linux internals, language semantics and system design, with a final day for full run-throughs and the leadership conversation. Days attach the reported questions and a few PracHub practice drills.

Small steps. Visible outcomes.0 / 7 completed
ONE WEEK · YOUR PACE

Prepare, practise & reflect

One practical outcome each day. Spend longer where you need it.

0 / 7 done
01Write the essay answers
  • Write full answers on why Canonical, your proudest technical achievement and a leadership role, each built on a specific project with a decision and a result
  • Write an honest answer on your mathematics and physics background and an outline of your open-source use
  • Proofread each answer aloud, then once more on paper

Deliverable: Four proofread written answers (motivation, proudest achievement, leadership role, academic background) and a one-page outline of your open-source use.

Practice prompt ↗Practice prompt ↗Practice prompt ↗Practice prompt ↗
02Cognitive speed and the open-source story
  • Run timed practice sets in number comparison, visual difference spotting, synonyms and antonyms, and mirrored shapes, recording accuracy per area
  • Write a three-minute spoken answer about your experience with Ubuntu and other open-source products, with one concrete episode
  • Record the answer and listen for name-dropping without detail

Deliverable: A scorecard of accuracy per GIA area and a recorded answer on open-source experience with one detailed episode.

Practice prompt ↗
03Boot, processes and pipes
  • Explain the boot path from firmware to user space aloud, naming what each stage hands to the next
  • Explain processes versus threads, fork and exec, and how the kernel schedules runnable tasks
  • Explain how the shell creates a pipe, forks the commands and wires file descriptors, then demonstrate it with a short C or Python snippet using pipe, fork and dup2

Deliverable: A boot-sequence diagram, a process-versus-thread comparison, and a working pipe/fork/dup2 snippet.

Practice prompt ↗Practice prompt ↗Practice prompt ↗
04Production Linux: upgrades, memory, troubleshooting
  • Write a kernel upgrade and rollback plan covering staging, keeping the previous kernel bootable, health checks and the rollback trigger
  • Explain virtual memory, overcommit, page cache and swap, and how the OOM killer picks a victim
  • Write a diagnosis sequence for high load average with low CPU utilization, naming the tool you would run at each step
  • Work the PracHub practice drill on instances that grow 400 MB per hour until a nightly restart

Deliverable: A kernel upgrade runbook, a one-page memory and OOM explainer, a troubleshooting checklist, and a written answer to the memory-growth drill.

Practice prompt ↗Practice prompt ↗Practice prompt ↗
05Language semantics and coding
  • In your primary language, write the ISBN check-digit validator with tests for hyphens, a trailing X, wrong length and non-digit input
  • Implement a concurrent LRU cache and explain your locking choice, then outline a Python log parser that outputs a JSON report
  • Write one-paragraph explanations of decorators, goroutines against OS threads, and Rust ownership, borrowing and lifetimes, with a code example for each
  • Tabulate hash-map and slice or list costs: lookup, append, resize and memory overhead

Deliverable: Tested ISBN validator and LRU cache code, three one-paragraph semantics explanations with examples, and a complexity table.

Practice prompt ↗Practice prompt ↗Practice prompt ↗Practice prompt ↗Practice prompt ↗
06Infrastructure design
  • Design the package-manager metadata service: read and write paths, mirroring or caching, consistency and what happens when a region is down
  • Design the IoT monitoring agent: resource budget, collection interval, batching, offline buffering, authentication and updates
  • Design the API gateway (rate limiting, authentication, dynamic routing) and the hybrid-cloud microservice deployment system, each with a failure-mode list

Deliverable: Four design write-ups, each with requirements, components, trade-offs and a failure-mode list.

Practice prompt ↗Practice prompt ↗Practice prompt ↗Practice prompt ↗
07Full run-through and leadership conversation
  • Answer the proudest-achievement and leadership questions aloud from your written versions, then add what changed afterwards
  • Practise the PracHub behavioral drills on telling callers their integration will break and on shipping under a deadline
  • Write your questions about contract status, tax treatment and in-person sprint travel, and re-read all your written answers for consistency

Deliverable: Two recorded spoken stories, two drill answers, and a written list of practical questions for the final conversation.

Practice prompt ↗Practice prompt ↗Practice prompt ↗Practice prompt ↗

Expand any day for tasks and deliverables. Your progress is saved on this device.

The reported behavioural material comes mostly from the written questionnaire: your motivation, your proudest technical achievement, a leadership role, your academic record and your experience with Ubuntu and other open-source products. The same stories are likely to resurface in live conversations, so keep them specific enough to survive follow-up questions and consistent between what you wrote and what you say.

What is your experience with Ubuntu and other open-source products? Which ones do you use regularly?

medium
Academic & Personal Background (The Writ

What is your experience with Ubuntu and other open-source products? Which ones do you use regularly?

Approach
  1. Open with an honest inventory: name the Ubuntu releases or other open-source projects you really use, in what setting (laptop, servers, CI, containers), and for how long. Only name what you can discuss in depth, because every name invites a follow-up.
  2. Pick one episode that shows engagement beyond installing and using: a bug you reproduced and reported, a patch or documentation fix you sent, a package you built or maintained, or a time you read the source to explain behaviour. State the situation in two sentences, then spend the rest on what you did.
  3. If you have contributed, describe the mechanics: how you found the project's contribution process, what a minimal reproducer or test looked like, how review feedback changed your change, and what finally happened. If you have not, describe depth of use instead, such as debugging a failed service with the system journal, pinning or holding a package version, or working out why a containerized app behaved differently.
  4. Close with what the experience changed in how you work, such as writing clearer issue reports or reading upstream changelogs before upgrading. Avoid the common failure of listing tools without a story or overstating a one-line fix as a major contribution.
Follow-up
  • Walk me through a bug report or patch you sent: what did you include, and what did the maintainers ask you to change?
  • What is one thing about Ubuntu or a tool you use that you would change, and how would you go about proposing it upstream?
  • How did you handle review feedback you disagreed with?

Tell callers you do not own that their integration breaks

medium
deprecationcompatibilitystakeholders

A field in a write endpoint's response must change shape. You own the endpoint; you do not own the four internal callers or the outbound webhook consumers who read it. Describe a deprecation you were responsible for: what you shipped first, how you established who was actually reading the field, the window you gave and what set its length, what you did about the consumer who never moved, and how you decided removal was safe. Name the signal you used, not the announcement you sent.

Approach
  1. Establish the reader set empirically rather than from a wiki of owners: per-field usage counters keyed by principal, or access logs attributed to a consumer. State the blind spot of whichever you pick, since a consumer that reads the field only on a monthly job will not appear in a week of logs.
  2. Ship additive first. Populate the new field alongside the old one so no reader is forced to move, which is also what keeps a rolling deploy safe, because old and new instances answer the same requests at the same time and a rollback must still find the old shape present.
  3. Set the window from the slowest legitimate consumer's release cadence, not from your calendar, and decide separately what to do for a consumer with no release process at all, such as an external webhook endpoint you can only email.
  4. Convert silence into evidence before you rely on it: a short, low-traffic removal window that makes a still-dependent consumer fail visibly and loudly while you are watching, rather than at three in the morning after you have moved on.
Follow-up
  • How would you detect a consumer that reads the field only during a monthly export?
  • One caller refuses to move and has a commercial relationship behind it. What changes in your plan and what does not?

Ship under a deadline and bound the debt you chose

medium
paginationtechnical debttradeoffs

You have four days to ship a tenant-facing listing endpoint. The version you would defend uses keyset pagination over (tenant_id, status, updated_at DESC, resource_id DESC); the version you can finish uses LIMIT/OFFSET with no matching index. Describe a deadline call you actually made of this shape: what you shipped, what you knowingly deferred, how you bounded the damage with a mechanism rather than an intention, and the specific numeric condition that would force the follow-up. Name who you told and where you wrote it down.

Approach
  1. Name the deferred failure precisely instead of calling it slow. OFFSET n makes the database produce and discard n rows, so cost grows with page depth; without an index matching the sort, every matching row is read and sorted before the limit applies; and rows inserted between two page fetches shift across the boundary so items are skipped or repeated with nothing in the response to signal it.
  2. Bound the blast radius with something mechanical rather than a promise: cap maximum page depth, cap page size, restrict the endpoint to one internal caller, or keep it behind a flag. State which failure each cap removes and which it leaves standing.
  3. Attach a number to the trigger and wire it to an alarm: the first tenant crossing N resources, or the endpoint's p99 crossing its share of the 400 ms budget, so the debt announces itself instead of waiting to be remembered.
  4. Write it where the next engineer looks, which is the code and the ticket, not a chat message: what was deferred, why, the cap, and the trigger.
Follow-up
  • At what page depth does the offset version breach your latency budget, given your page size and row counts?
  • What breaks first when you switch to keyset pagination later, and what does a client holding an old page token see?
  • 01

    What is your experience with Ubuntu and other open-source products? Which ones do you use regularly?

  • 02

    What is your proudest technical achievement as a Software Engineer, and what specific challenges did you overcome?

  • 03

    Describe a leadership role you took on during your education or early career and the impact you made.

  • 04

    Tell me about a time a maintainer or reviewer criticized your technical work. What did you change, and what did you leave as it was?

  • 05

    Describe a project where you worked mostly in writing with people in other time zones. How did you keep decisions clear and traceable?

PracHub preparation framework ↗
How many rounds are there and how long does the process take?

Candidates report 4 rounds: an Asynchronous Assessment, Technical Coding Challenges, Domain-Specific Interviews and an Executive Leadership Review. They describe about 3-5 weeks, and some say it can run to months, so plan your availability and do the early written stages when you can concentrate.

Canonical Software Engineer candidate reports ↗
Why are there questions about school grades in mathematics and physics?

Candidates report that the written questionnaire asks about performance in mathematics, physics and science at school and university, sometimes including where you ranked. Prepare a plain, truthful answer: what you studied, how you did, and how that background shows up in how you write software today.

Canonical Software Engineer candidate reports ↗
Can I use an AI tool to write the essay?

Candidates report that generic, AI-sounding essays go badly and that specific personal detail is what stands out. Use a tool at most to check grammar; write the content yourself from real projects, decisions and results, and make sure you can discuss every sentence aloud later.

Canonical Software Engineer candidate reports ↗
What is the Thomas GIA and how should I practise for it?

Candidates describe it as a timed cognitive test taken before speaking to a recruiter, covering perceptual speed, number speed and accuracy, word meaning and spatial visualization. Practise each area under a timer, track accuracy, and aim to answer what you can correctly rather than guessing across the whole test.

Canonical Software Engineer candidate reports ↗
Which languages and topics should I prepare?

Candidates report Python, Go or Rust depending on the team, with C/C++ also listed for the role, alongside Linux internals, Bash, containers (Docker, LXD), Kubernetes and packaging (Debian packages, Snaps). Choose one primary language for coding, learn its runtime behaviour well, and study Linux mechanisms at the level of system calls and commands.

Canonical Software Engineer candidate reports ↗
What do candidates report about remote work, travel and contract terms?

Candidates describe a fully remote company with international travel to in-person sprints at least twice a year, a bring-your-own-device arrangement for many roles, and contractor status in some countries. Ask early about tax implications, contract type and expected travel so you can decide before investing in the later rounds.

PracHub Software Engineer practice ↗
How much open-source experience do I need?

Candidates report that public contributions and community participation are valued, and that Ubuntu and other open-source use comes up in the written stage. If your contributions are small, prepare an honest, detailed account of one bug report, patch or package, or of how deeply you use and debug open-source tools.

Canonical Software Engineer candidate reports ↗
Sources & methodology 3 sources ↗

No official company page is cited. Rounds and questions come from candidate reports and PracHub editorial material; each source shows the date it was read.