Daniel Locatelli logo
A man uses the funding calculator on an iPad.

KfW Funding Calculator by BuildSystems

Date 2023-11-20
Author BuildSystems
Client BuildSystems
Director Martin Bittmann
Manager Julia Dorn
Team Daniel Dieren, Daniel Locatelli
Place Online
Link https://app.buildsystems.de

Features

  • Price estimation of a building: The app estimates the cost of a new construction or renovation based on publicly available data from Arge e.V.
  • Loan Simulation: Accurate simulation of loans based on the price estimation and energy efficiency of a building.
  • Energy metric inputs: Influencing the subsidy and loan possibilities.
  • Data Security: Ensuring all user data is secure and follow EU regulations.
  • Responsive Design: App works seamlessly across all devices.

Tech stack

  • GitHub: For the Git repository.
  • Angular: A modern JavaScript framework backed by Google and used for large-scale applications.
  • ng2-charts: Angular wrapper for the Chart.js library. It is used to create responsive and interactive charts.
  • Cloudflare: Hosting provider to ensure reliability and scalability, no initial investment is required.

Why are we building this toolbox?

Germany is known for pushing green tech, such as solar panels and wind turbines, through public subsidies. But did you know that there are also many subsidies for energy-efficient construction? Although these subsidies are attractive, navigating the bureaucracy can be incredibly challenging.

This app developed by BuildSystems makes it easy to simulate a loan from the national bank KfW. It simplifies the process by offering a user-friendly interface, allowing real estate developers and homeowners to understand their financial options quickly and easily.

Development Process

The app development happened in three major phases. The Planning and Design, the Frontend Development and the Test & Quality Assurance.

Planning and Design

To begin the project, the whole team defined the requirements and variables for the app logic. Once that was settled, I sketched the frontend architecture and the UI/UX.

App logic

Daniel Dieren developed the app logic in Excel. My role was to review it, reverse engineer the formulas to ensure everything was correct, and suggest improvements. This step overlapped with the whole software development process because, as we advanced, we noticed that we could add more relevant information.

I created a simple documentation on Notion from the Excel file to understand each formula thoroughly and make it easy to bring it to TypeScript later.

Frontend Architecture

Because the team already uses Figma, I decided to stay within its ecosystem. So, I used FigJam to sketch an initial software architecture diagram, thinking about which components would be necessary and the relationship between them.

%%{ init: { 'flowchart': { 'curve': 'basis' } } }%%
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
flowchart TB

	R(Routes) --> N & S & PO & PR & SE
	
	subgraph S[Sanierung Component]
		SF
		SS
		SOUT
		SSV
	end
	
	subgraph SOUT[Output]
		SD
		SCH
	end
	
	SF(Sanierung Forms)-->SS(Service)
	SS-->SD(Dashboard)
	SS-->SCH(Charts)
	SS-->SSV(Save)
	
	subgraph N[Neubau Component]
		NF
		NS
		NOUT
		NSV
	end
	
	subgraph NOUT[Output]
		ND
		NCH

	end
	
	NF(Neubau Forms)-->NS(Service)
	NS-->ND(Dashboard)
	NS-->NCH(Charts)
	NS-->NSV(Save)
	
	subgraph PR[Profile Component]
		CP(Change password)
		DC(Delete Account)
	end
	
	subgraph SE[Settings Component]
		CT(Change Theme)
		CL(Change Language)
	end
	
	NL-->NLD
	SL-->SLD
	
	NSV-->DB[(Database)]
	SSV-->DB
	
	DB-->SL
	DB-->NL
	
	subgraph PO[Portfolio Component]
		NL(Neubau List)
		SL(Sanierung List)
		SLD(Load)
		NLD(Load)
	end
Diagram of the app architecture (in mermaid).

UI & UX Design

Conceptually, my approach for the design was to focus on simplicity and functionality, aiming to guide users through the loan simulation process with minimal friction.

To create prototypes, I used Figma, which was quite a pleasant design experience. Simulating mouse over, mouse in, mouse out behaviors is possible. Besides, their paid plan makes copying CSS styles and SVGs easy with the Dev Mode. But even with the free plan, it feels like a breeze to export SVGs.

Screenshot of the desktop version of the funding calculator.
Desktop version at Sanierung/Projekt.
Screenshot of the mobile version of the funding calculator showing the Neubau at the Projekt tab.
Mobile at Neubau/Projekt.
Screenshot of the mobile version of the funding calculator showing the Neubau at the Darlehen tab scrolled to the graphs section.
Mobile at Neubau/Darlehen.

Building the User Interface

This project marked my metamorphosis into a fully-fledged software developer. This required me to learn Angular, a JavaScript framework, and its highly opinionated structure, which was perfect for my case.

Why did we choose Angular?

Many people believe that Angular is one of the most difficult frameworks for web development. If we compare it to React or Vue, for example, it does looks harder in the beginning. But the truth is that because Angular has a lot of built-in features (which means it is "opinionated"), it eliminates the need to make a lot of decisions later on. So, I could skip the most painful part for a solo learner: the mental exhaustion that can come from making too many choices. And believe me, decision fatigue is a real thing!

In addition, Angular uses a programming language called TypeScript. Think of it as JavaScript with a code checker that helps catch errors before they become problems. Since I was the only person writing the code, TypeScript was a big safety net. In fact, I would only trust myself to develop this app with the guardrails that TypeScript creates. Remember that I had no code reviewer; I was a team of one on the software side.

Another reason to choose Angular is its reputation for reliability and ease of maintenance, especially in large-scale applications. It is backed by Google, which have already built more than 2600 solutions with it, so it is clear that it can handle complex projects and will be maintained for a long time.

With my background in architecture and engineering, I understand how quickly things can become complex in this field too, and even though the KfW Calculator seems simple at first, it includes around a couple of hundred variables and over a hundred functions in its first version. Given BuildSystems' goal to create a scalable app that will evolve into a comprehensive early planning tool, Angular's strengths were a perfect fit for this project.

AI Tools as a Copilot

It is also worth mentioning the importance of AI tools as a copilot. ChatGPT played a crucial role converting the excel formulas into TypeScript code. But I have to say though, for cutting-edge features these AI tools did not give good answers, because obviously they still don’t have the training data available.

Process

During the development process, I tried to create a single component that would accommodate both the New Building (Neubau) and the Renovation (Sanierung) calculator, making the code less repetitive following the principle of DRY (don’t repeat yourself).

This, however, made the component too complex because there were many variables and requirements unique to each calculator. So in the end I decided to split it into two components. Although there is some redundant code, this added speed to the development.

%%{ init: { 'flowchart': { 'curve': 'base' } } }%%
flowchart TB
	NC(Neubau Component) --> NPFC(Projekt Form Component) & NDFC(Darlehen Form Component)
		subgraph NPF[Projekt Form]
			direction TB
			NPFC --> NPFS(Projekt Form Service)
		end

		subgraph NDF[Darlehen Form]
			direction TB
			NDFC --> NDFS(Darlehen Form Service)
		end
	NDFS --> NS(Neubau Service)
	
	NPFS --> NS
	NS --> NPD(Projekt Dashboard)
	NS-->NPCHC(Charts Component)
	NPCHC-->NCHG(Gesamtkosten Chart)
	NPCHC-->NCHG2(Gesamtkosten m² Chart)
	NPCHC-->NCHE(Einheitskosten Chart)
	
	subgraph NPCH[Charts]
		direction TB
		NPCHC
		NCHG
		NCHG2
		NCHE
	end
	
	subgraph NPOUT[Output Projekt]
		direction TB
		NPD
		NPCH
	end
	
	NS-->NDD(Darlehen Dashboard)
	NS-->NDCHC(Charts Component)
	NDCHC-->NCHA(Annuitäten Chart)
	NDCHC-->NCHF(Finanzierungskosten Chart)
	NDCHC-->NCHT(Tilgung Chart)
	subgraph NDOUT[Output Darlehen]
		direction TB
		NDD
		NDCH
	end
	
	subgraph NDCH[Charts]
		direction TB
		NDCHC
		NCHA
		NCHF
		NCHT
	end
	
	NS-->NSV(Save Option)

	NPF:::paddingNPF
	NDF:::paddingNDF
	NPCH:::paddingNPCH
	NDCH:::paddingNDCH
	NDOUT:::paddingNDOUT
	classDef paddingNDCH padding-right:34em;
	classDef paddingNPCH padding-right:37em;
	classDef paddingNPF padding-right:9em;
	classDef paddingNDF padding-right:9em;
Diagram of the Neubau component.

The strategies used to implement the features also varied throughout the development because as I progressed, I learned new and improved ways to achieve the same result. For example, the implementation of the forms changed twice already. The first time I decided to refactor the whole code to make sure everything was homogeneous and had a more clear code.

That, however, turned out to be a poor product management decision, because the features were already working and although the code was a bit confusing, changing the internals would not affect the end user at all. So for the second change, which is happening for the second version of the app, I won’t be refactoring the rest of the code.

If you want to learn more about how I am currently implementing the forms, check out this article by Zoaib Khan:

Testing and Quality Assurance

I also went down the rabbit hole on the topic of unit testing using the default Karma tool.

This type of testing checks small parts (units) of the software to make sure each one works correctly on its own. Unfortunately, though, I could only learn this at a late stage, which meant I had to refactor the code to allow the unit tests to work.

Besides, if I had to start over, I would instead focus my energy on end-to-end testing (E2E) using Cypress. This test checks the whole system by simulating user interaction, ensuring inputs and outputs match our due diligence.

Deployment

We initially deployed the app on Netlify because they have a super smooth experience. Their business model is “pay as you scale” with no initial costs. Besides, it is a no-code deployment solution; you just connect your GitHub repo, and they do the rest!

However, some Netlify users started to spread how they went from the free plan to being charged tens of thousands of dollars, or even $104K in a month. All because of a DDOS attack that could happen to anyone. Because Netlify did not have a DDOS attack prevention mechanism, we decided to move to Cloudflare.

Cloudflare is similar to Netlify. Same business model and automated deployment using GitHub. However, it does have a more robust anti-bot system.

The deployment is automatic and quite simple:

%%{ init: { 'flowchart': { 'curve': 'base' } } }%%
flowchart LR
NG(Angular) --push--> MA(main branch) --> CP(CF pages production) --> PA(https://app.buildsystems.de)
NG --push--> DV(development branch) --> CD(CF pages development) --> DA(https://branchname.pages.dev)

subgraph VS[VS Code]
	NG
end

subgraph GH[GitHub]
	MA
	DV
end

subgraph CF[Cloudflare]
	CP
	CD
end

Ensuring a Smooth App Development: Key Insights and Strategies

  • Security First: Prioritize data security from the beginning to avoid compliance issues later.
  • Early Planning: Invest time in planning and understand the requirements you need before starting the development.
  • AI Tools: Utilize AI tools for initial designs to quickly generate user interfaces; use code copilots even if just ChatGPT.
  • Flexibility: Be aware that the app will evolve, so don't adhere too strictly to the DRY principle.
  • Framework Selection: Choose a framework that suits your project's needs and stick with it. Usually, the best framework is the one you already know.
  • Continuous Testing: bet your testing efforts on end-to-end testing.

One of the main challenges was to make the app "snappy"; in other words, while the user moves a slider, all the values and charts are updated in real-time. Also, we were cautious about the users' data because of the super restrictive EU regulations.

The first decision was to avoid server-side calculations altogether. The whole app is client-side only, which means that once it is loaded, it doesn't have to send data anywhere; the calculation happens directly on the device. That means we also didn’t have to worry about data storage for the first version.

Designing the app from scratch was a valuable experience, but now that I understand the process, I would start the UI design with an AI assistant. Tools like Galileo AI or Rendition Create can help start with a nice app interface generated from prompts (Text to UI). Starting with a UI draft is always faster, even if the draft changes drastically.

Right now we are working on the second version of the app. The idea is to bring another calculator and some other features, like saving a project and comparing two projects. We will be using Supabase to store the data.

erDiagram
    auth_users {
        uuid id
    }

    neubau_projects {
        bigint id
        text title
        uuid created_by
        timestamp created_at
        uuid owned_by
        uuid last_edited_by
        timestamp last_edited_at
        text other_project_values
    }

    sanierung_projects {
        bigint id
        text title
        uuid created_by
        timestamp created_at
        uuid owned_by
        uuid last_edited_by
        timestamp last_edited_at
        text other_project_values
    }

    einzelmassnahmen_projects {
        bigint id
        text title
        uuid created_by
        timestamp created_at
        uuid owned_by
        uuid last_edited_by
        timestamp last_edited_at
        float vollkosten
        float bafa_foerderung
    }

    user_neubau_projects {
        bigint id
        uuid user_id
        bigint project_id
    }

    user_sanierung_projects {
        bigint id
        uuid user_id
        bigint project_id
    }

    user_einzelmassnahmen_projects {
        bigint id
        uuid user_id
        bigint project_id
    }

    einzelmassnahmen_items {
        bigint id
        bigint project_id
        text title
        int position
    }

    einzelmassnahmen_values {
        bigint id
        bigint item_id
        bigint project_id
        text title
        float value
        text unit
        int position
    }

    auth_users ||--o{ neubau_projects : "created by"
    auth_users ||--o{ neubau_projects : "owned by"
    auth_users ||--o{ neubau_projects : "last edited by"
    auth_users ||--o{ sanierung_projects : "created by"
    auth_users ||--o{ sanierung_projects : "owned by"
    auth_users ||--o{ sanierung_projects : "last edited by"
    auth_users ||--o{ einzelmassnahmen_projects : "created by"
    auth_users ||--o{ einzelmassnahmen_projects : "owned by"
    auth_users ||--o{ einzelmassnahmen_projects : "last edited by"
    auth_users ||--o{ user_neubau_projects : "has many"
    auth_users ||--o{ user_sanierung_projects : "has many"
    auth_users ||--o{ user_einzelmassnahmen_projects : "has many"
    neubau_projects ||--o{ user_neubau_projects : "has many"
    sanierung_projects ||--o{ user_sanierung_projects : "has many"
    einzelmassnahmen_projects ||--o{ user_einzelmassnahmen_projects : "has many"
    einzelmassnahmen_projects ||--o{ einzelmassnahmen_items : "contains many"
    einzelmassnahmen_projects ||--o{ einzelmassnahmen_values : "contains many"
    einzelmassnahmen_items ||--o{ einzelmassnahmen_values : "contains many"