generating my résumé because i'm unemployed
it's not about LLMs, i sweari am as of writing this a third year computer science student and i need a job. otherwise, i won’t be able to complete my degree in a two-to-three years’ time. thus, i must be forward-thinking and competitive. what’s a good (fun) way to get on top of the game? well, generating your résumé using typst, of course!
by “generating” i mean that i am putting down lots of effort up-front now, so that i can be lazy in the future and simply add the relevant info i want to show when applying for a job. it’s automation by code. you’ll see what i mean by the end of this article. in short, no pesky LLMs doing all the hard work creating a résumé for me.
the idea
i have been using typst for quite some time to typeset my documents, for school or otherwise. my last résumé i wrote in latex, but i don’t wish to repeat the same mistakes (i don’t like latex, it is clunky), so i decided to make my new, updated résumé in typst.
but what if i want to apply internationally? i should probably maintain an english and a norwegian version of my résumé. this multiplies the amount of paperwork by a factor of two! yuck! we need to work around this.
lastly, in my short time on earth i have procured enough experience to exceed the 1 page limit for résumés, so i need to figure out how to shorten my document – to compact it as neatly as possible.
the execution
luckily, our math course has forced us to typeset our documents this semester, so i have a pretty, little typst template ready to fire. so, i repurposed this document to make a pretty, little document as a start. i wrote some code for pretty, little boxes to surround my headers and i modified the default table of contents to show these pretty, little boxes. i used tables to align things and neatly lay out the most important information on the first page.
wait, table of contents? for a résumé? yes, that is peculiar. quite strange. i am aware of what a résumé is, right? – okay, so i lied. we are making a CV (for now). i will make the distinction that a résumé is short and concise, while a Curriculum Vitae is long and complete.
so after hacking on this document, it sure looks great. there is just one problem: there is nothing to look at! we need to populate the skeleton with some content about me. so after filling in some info, i realized that the data itself is repeatedly organized as an element with some style modifications and inserted into a list. thus, we can over-engineer: enter typst’ content functions.
in typst, we can easily define functions that produce content. for example
#let foo(bar) = [#underline(bar)]
#foo[hello]
this function takes in some content and reproduces the same content, but with a line under it. the resulting content can be further modified through a pipeline of such transformations, or simply placed in the document wherever the function was called.
with this, i can create a function that takes in some strings of information, which in turn spits out neatly organized content, ready to be displayed proudly. thence we get
#let info_card(title, position, location, period, body) = [
#let name = [#text(size: FONT_SIZE * 1.1, weight: "bold")[#title]]
#let date = [#text(size: FONT_SIZE * 0.8)[#period]]
#let place = [#text(size: FONT_SIZE * 0.8)[#location]]
#let title = [#text(size: FONT_SIZE * 0.9)[_#position _]]
#name #h(1fr) #date \
#title #h(1fr) #place \
#body
]
this is an excerpt from a simple function i used to organize some input data
using relative spacing (#h(1fr)) together with some style modifications. now
i can easily add data to the document in a consistent style through a few
function calls to info_card. note that i am also using a few show-rules
globally, which simplify this function quite a bit, but i won’t talk about those
here. they are really neat though!
now, the problem is localization. how can i create my document in both english and norwegian? my brilliant idea was to use json. yeah…
i created a json file to act as a single source of truth, containing two
attributes at the top, namely us and no. then i filled these in with data in
a structured sense that reflected my document, like
us.section.work-experience.body.job1.location
== "[where i worked at job1]" // arbitrary data
this was a bit of work to do, but now my typst document contains no raw data, only some code to read from the json (which typst handles beautifully).
i can now just switch language by switching a constant from "us" to "no" and
vice versa. this also changes the text in the document to be recognized as of
a different language, thus changing the linebreak behavior, i.e. which words to
split up is based on the current language. fantastic.
what we have so far is a grand document consisting of the skeleton (typst) and the data (json). this is the aforementioned CV, the path of my life. we now need to create the résumé.
to do so, we must optimize the skeleton by compactifying content. i did this by tuning the font size down a bit, and utilizing a two-column layout for maximizing the data on display. this worked wonders, because my layout had already been created in a quite flexible fashion using the content functions. thus, i was able to get almost all my experience onto one page without needing to constrict the data flow!
however, we still want to limit what data is being displayed. for this, i created a filtering function to exclude certain things from the json that we do not want to display. i can easily tweak this for the job i am applying for, to only include the relevant data. for now, this function just works on a list of json keys to remove from the loaded-in-memory dictionary containing all the json data in typst. it simply lowers the amount of data being shoved into the rest of the program, which dynamically renders the document using the aforementioned content functions.
so by omitting needless data and compactifying the skeleton, we achieve a neat résumé, “generated” from a single source of truth - json - with just the data we want, and also in the target language. nice.
the aftermath
this took a while to do, but now i won’t have to worry about changing much other than adding some more data as i gain more experience. i will always have both a CV and a résumé ready, should i need it. o glorious peace of mind!
further work on this project could include creating a front-end for more easily selecting the data i want to add to the output. at the same time, i could also add a front-end for adding or updating data. basically, make it easier to interact with the data. i could consider using a database even. maybe i could use an LLM to maintain parity between the two language translations, such that i don’t forget to update the other after updating one. though, this could be enforced through a front-end without the use of LLMs, but then i would still have to be the one to perform the translations.
other than that, i’m quite happy.
did i mention i used nix to manage the dependencies? it makes it easy to compile the document.
じゃあ、またね!