Hi 👋 I'm Krystof
Fullstack software engineer
I'm a detail-oriented software engineer with 6 years of experience in fullstack web development working with TypeScript, React, Ruby.
Read about my professional contributions, skills, and more below or see my resume.
Professional contributions
2025-2026
TerminalIn shipping, vessels transport containers between ports. A terminal is a place in a port where containers are loaded and unloaded from the vessel. integrations
I've integrated Portchain's data platform with terminals from Central America and Southern Europe using REST APIs. See more... These integrations periodically check for updates from terminals, match their schedules with Portchain's data and synchronize between the two.
Other
- Implemented features for either a bet to acquire new customers or commitment to existing customers.
- Played a role in migration from Heroku to Google Cloud Platform.
- Modernized the codebase by introducing Dependabot and updating dependencies.
- Participated in hiring process of three engineers in about 10 job interviews.
2022-2025
Funding rounds calculator
I have driven increased product demand by developing a calculator that helps founders model unpriced funding rounds. See more... The solution involved an RFC Request For Comments-like process to come up with the best solution as this was the first public page (i.e. not behind auth, obviously excluding a sign in/sign up flow). At the time we were also in a rebranding transition where the homepage was already transitioned and the idea was to use the new design on all public facing pages so the calculator should also follow the new design. It uses many primitive components and other more complex components some of which I helped building. Check it out for yourself!
Billing revamp projectwar story
I enabled better sales offering and streamlined customer billing management by revamping billing in a Ruby on Rails app integration with Stripe and HubSpot. See more... This was the longest running project I worked on and wrote around 90% of it when it comes solely to the changes in the app codebase. First step was a migration from one Stripe instance to anotherThis was supposed to be relatively simple. But it's Stripe we're talking about 🙂 I've ran into most of the issues described in that video and many more during this project. Migrating from one instance to another is a hell that made me into the man I'm today.. The next step involved changing how the app is interacting with Stripe where previously it was the app recording changes to customer subscriptions using Stripe's API and now the idea was to make these changes from the self-service customer portal and update the app data via webhook events from Stripe. It also involved a change to the pricing packages where there was previously a single package and additional customer features were enabled on an ad-hoc basis and now there would be multiple packages with certain pre-enabled features.
Fundraising for early-stage startups
This project helps early-stage startups in fundraising and offering them inexpensive assistance on tax relief to ensure their success. See more... Services included are round modelling calculator, creating and filing advance assurance, SEIS & EIS, and templates for ASAs & SAFEs. Major challenge in this project was a tight deadline that was agreed on few months in advance and that had to be met which I achieved without sacrificing quality.
In-app workflows
The in-app workflows I implemented increased number of requests for an additional paid service from ~5 to 20+ a month increasing revenue in this area by 300%. See more... These included a backoffice part, where our colleagues fulfilling the request were able to manage and update progress on the requests, and the end customer part, where customers were able to request these services.
Currency exchange rate provider
I provided affordable currency exchange rate solution for 200+ currencies. See more... While the app had a multi-currency support from the time I joined, the currencies that were supported were those available from European Central Bank which contains 30 currencies. Meanwhile, there are 180 currently circulating currencies recognized by UN and some of those started to appear in the app and it became cumbersome to deal with them manuallyThe exchange rate for the missing currency had to be found and manually added to the database. As we had to do this via a manual task due to compliance, it was anything but fun. Not to mention this exchange rate became out-of-date soon and would result in incorrect conversions. So we had to bite the automation bullet eventually.. I used Exchangerates API to get a more comprehensive list of the exchange rates. As we didn't call the API everytime we needed the exchange rate but instead stored it in a database (we also needed historical exchange rates in some cases), new exchange rates were automatically added to the database on a daily basis.
Automated invalid link checker
Addressed invalid links by automatically checking and reporting them. See more... An external link can change and suddenly you're linking to a non-existent page. At some point we encountered a few links that were invalid and instead of just fixing those and moving on with our life I wrote a script to check external linksI thought this would be a matter of simply curling the page with a parameter to return the HTTP response status code. How naïve I was. I had to use curl-impersonate to mimic a browser making the request for it to go through on many pages. There was also the question of redirecting where for example Zendesk (Support Center) puts an ID as well as the name of the support article into the URL so when the name of the article was adjusted the link still worked but returned a redirect 3xx code. I made curl-impersonate return the redirect URL as well and included it in the result message. that I set up to run on GitHub Actions on a weekly basis and send a result to an #automated-checks Slack channel. In this way, these issues became more visible and otherwise invalid links that would be in the app for many months were fixed within a week.This check has been implemented for this blog as well.
Improved modal component
Improved both UX and DX by improving a modal component. See more... The way we were writing most of the modals was by directly using the design system components as opposed to a more complete modal implementation. Every colleague therefore had a lot of freedom when it came to implementation of a modal. Properly implementing a modal so that it's consistent with design and behaves in the least obstructive way possible then becomes hard. Our modals were incosistent with how disabled and loading states were handled after clicking cancel and submit buttons, layout of those buttons was sometimes different and when it came to modals with forms, the form being reset after a successful submit caused a flicker while the modal was fading out which was visually distracting. I implemented a modal component to make implementing consistent modals simpler, including support for forms, where all of the aforementioned concerns were handled by default. These models can be seen in action in the funding rounds calculator above.
Data integrity checks
Maintained product quality by implementing data integrity checks. See more... My colleague implemented a framework for adding data integrity checksWe had a policy for no check constraints in the database so while some of these checks could have been handled differently, this was still a better solution for majority of the checked cases due to the sheer complexity of the domain and the resultant data. to notify us of instances where invalid data got into the database and also automatically fix them if an automatic fix is possible. I wrote checks dealing with detecting and autofixing trailing whitespace in attributes like names, emails and domain specific identifiers which could cause subtle bugs in filtering and matching items.
Linter rules
Maintained coding conventions and simplified codebase using lintingA process and a tool for code analysis to flag programming errors, bugs, stylistics errors and suspicious constructs. rules as well as prevented subtle bugs. See more...Before
key :required, [:id, :created_at] property :id do key :type, :integer end property :created_at do key :type, :string key :format, 'date-time' end property :status do key :'$ref', :StatusEnum end property :values do items do key :type, :number end end
After
DocsApiHelper.properties({ id: :integer, created_at: { type: :string, format: 'date-time' }, status?: :StatusEnum, values?: { type: :number, array: true } })
"This::Constant::Exists" ... "But::ThisOne::DoesNot"
Maintained coding conventions and simplified codebase using lintingA process and a tool for code analysis to flag programming errors, bugs, stylistics errors and suspicious constructs. rules as well as prevented subtle bugs. This involved playing around with ASTsRead more about Abstract Syntax Trees for some light nighttime reading.. I made linter rules for both frontend (ESLint) and backend (RuboCop). Frontend one was about linting a stylistic error to follow our coding conventions (i.e. no more pointing it out in code reviews). In the backend, I made two linting rules which are a tad more complex:
- OpenAPISpecification for a machine-readable interface definition
language for describing web services. helper enforcement - writing the OpenAPI specifications can be a bit cumbersome, there's
a lot of boilerplate that needs to be written making bigger types hundreds
of lines long. I wrote a helper to simplify this where majority of the use
cases that previously took anywhere from 1 to 10 lines could now be reduced
to a single line of code making it more readable. Converting the whole OpenAPI
specification to use this helper would a large effort for an individual
and there would still be the problem with my colleagues not being aware
of this helper and writing new specifications without it. This linter rule
therefore produced warnings (and errors in some cases)
to enforce its usage suggesting fellow colleagues to convert it to use the
helper. The idea was that the warnings would be changed to errors once the conversion is complete so that it would be written in this more
concise and readable format.
Before
key :required, [:id, :created_at] property :id do key :type, :integer end property :created_at do key :type, :string key :format, 'date-time' end property :status do key :'$ref', :StatusEnum end property :values do items do key :type, :number end end
Use DocsApiHelper.properties
After
DocsApiHelper.properties({ id: :integer, created_at: { type: :string, format: 'date-time' }, status?: :StatusEnum, values?: { type: :number, array: true } })
- Check strings looking like constants actually exist - in Ruby, we were sometimes forced to write a constant, such as a class
name, as a string. The static type checker for Ruby, called Sorbet, we used
obviously didn't check strings and such strings sometimes became obsolete
when the class was refactored or removed and could cause an issue. This
linter rule checked that all strings that looked like such constants actually
referred to an existing constant in the codebase.
"This::Constant::Exists" ... "But::ThisOne::DoesNot"
This constant does not exist. Did you make a typo?
Frontend to backend type validation
Increased developer's confidence and prevented minor bugs by ensuring frontend matches backend. See more... The OpenAPI specification was used to generate TypeScript types in the frontend. Since the OpenAPI specification was written by hand, there could be and were discrepancies between what was described in the OpenAPI schema and the actual shape of the response that the server returned. The idea of this initiative was to validate that the types in the frontend, that were generated from the OpenAPI specification manually defined in the backend, matched the actual response from the server and in the case it didn't match, report an error to Sentry which we used for frontend error tracking. As we didn't want to impose the cost of validating the types on every request in production, it was only enabled in our QA environment that we used often enough to catch majority of these errors.
Frontend type:
type Example = { id: number kind: 'good' | 'bad' }
Server response:
{
id: '123'
kind: 'neutral'
unknown: 'smth'
} Type validation output:
Property 'id' is not of type 'number'.
Property 'kind' does not match allowed values.
Unknown property 'unknown' found.
Conventional commit message auto formatter
- No expected format for branches or commits
- Conventional commits required for features
- Standardized branch names and conventional commits required everything recorded in Jira
- Every code change needs to have a Jira associated and hence will have standardized branch name and conventional commits
The commit message "example message" is autoformatted based on the branch name "feat/JIRA-123-example" to a final message reading "feat(JIRA-123): example message"
2020-2022 (full-time from Feb 2022)
Announce app releases on Slack
Freed up engineering lead time by automating app release announcements in Slack. See more... We used Heroku for hosting and deployed the latest Git main branch on every new change. We had a process in place where once a week one of the engineering leads compiled a list of all the changes with their Jira ID and a short description and posted this in a #release Slack channel. I implemented a task that ran on every deployment to Heroku which compiled a list of Jiras from the last deployment, got the Jira tasks descriptions, the author (developer) of that change, found a related Slack account and tagged them in the automated Slack message (developers were encouraged to provide additional context, especially when it was related to a bug they fixed for someone from a support of other department).
Grammar checker
Improved copy quality through automated grammar and spellchecking. See more... Using LanguageTool I implemented a check in GitHub Actions that checked grammar including spellchecks. This involved compiling the list of translations from a YML file into a human-readable form which is what LanguageTool expects.
Example output for copy "This is highlight by diferent colour.":
highlight - possible grammar mistake highlighted diferent - possible spelling mistake different colour - colour is British English color
Sentry (error tracking)
Enabled proactive identification and resolution of frontend issues by setting up Sentry for error tracking.
OpenAPI
Laid the foundation for future enhancements such as automated TypeScript type generation by adding OpenAPI. See more... Found the tooling and implemented the helpers for adding OpenAPI 2.0 (later upgraded to version 3.0) and implemented it on a few regular cases as well as some exceptional ones. This was used later by the team for generating TypeScript types for frontend which increased our efficiency. It led to other initiatives, such as my work on linters and frontend to backend type validation later on to improve our workflow and get as much as possible from the coupling of backend to frontend this afforded us.
2019-2020 (part-time)
Dependency management
Accelerated developer velocity for hospital task system app for nurses and orderlies by streamlining dependency management and build system. See more... The team had a strict policy for dependency management and actually maintained their own repository of dependencies. This was initially done through quite a large Git submodule. I've made it so that the team was able to use our own repository of dependencies as if they were using the original repository which simplified the workflow to a large extent.
Other
- Gathered data that were previously compiled manually, automated minor tasks.
- Maintained high software quality by participating in acceptance testing.
Skills
Frontend
+ Native
Router
Backend
DevOps
Cloud
+ Actions
Pages
AI & productivity
Development tools
Meetings & processes
- Stakeholder demos
- Code reviews
- 1:1s
- Hiring engineers
- Technical design meetings
- Performance reviews
- Pair programming
- Ideation sessions
- RFC-like proposals
- Retrospectives
- Daily standup
- Office hours
- Colleague mentoring
- Stakeholder demos
- Code reviews
- 1:1s
- Hiring engineers
- Technical design meetings
- Performance reviews
- Pair programming
- Ideation sessions
- RFC-like proposals
- Retrospectives
- Daily standup
- Office hours
- Colleague mentoring
Education