NewsTechTrends

Beyond simple productivity hacks: biggest value unlocks we’ve delivered with AI

Generative AI

The current hype around Generative AI (Gen AI) is undeniable. The euphoria has partly been fueled by tools like ChatGPT and Midjourney racing into mainstream adoption with millions of users worldwide, giving the masses, for the first time, a taste of what’s possible. These tools can deliver huge efficiency gains for the average knowledge worker and sometimes even for creative collaboration (here’s how Shann Puri, host of the popular podcast MFM uses ChatGPT to help him write movie scripts). 

At Calcey, while we’ve been keen to adopt these tools (here’s how we tried writing software product spec with ChatGPT), and we’ve always had the belief that focusing on solving core business challenges, rather than hyped-up tools is the way to unlock big value. Here are a few examples of key business challenges we’ve been tackling using AI.

AI to Improve Product Search, User Experience, and Conversion

For many businesses, the accuracy and ease of product search can significantly impact user experience and conversion rates. For e-commerce or online lead generation businesses this is a central part of their business and hence a better experience directly translates to a competitive advantage. 

One of our clients, Biocompare, is a leading resource for product information in the life sciences industry, hosting over 10 million products. The site’s users rely heavily on academic citations to make informed purchasing decisions. Hence, Biocompare had implemented a keyword-based matching system to automatically draw in citations related to listed products. This approach often resulted in inaccurate citations, hence it was fine tuned with filters that would remove many valid citations as well, leading to lost opportunities.

To address this problem, Biocompare’s internal team and Calcey collaboratively developed a transformer model architecture similar to ChatGPT’s, enabling context-aware matching of academic literature with products. In layman’s terms the new model looks at the surrounding words for a given keyword (i.e. the rest of the sentence) to understand the context and thereby what is being referred to. For instance, this enables us to identify whether it’s a company, product, disease, technique etc. 

Let’s illustrate what this means with an example. Let’s say we present the following sentence to our model “___________ is a major global health issue with more than 350,000 newly diagnosed cases per year with associated mortality rate reaching around 45%.” Given the context of the sentence our model would understand that “____” refers to a condition/disease. 

Specifically, we fine-tuned a pre-trained language model with a small, labelled dataset of academic papers. This approach is currently being trialed and shows promising signs that it can provide an order of magnitude more citations to users with higher accuracy. 

Streamlining Product Discovery

Another client, a major motor spare parts distributor, faced challenges with their multi-step product configurator, which was cumbersome for users. We realized that this multi-step search process can be greatly simplified with a bot that users could type their queries into using natural language and we started working on proving this hypothesis with a prototype. This client carries 30,000 spare parts spread across around 100 brands and 5,000+ models, hence this would be no small task. 

Calcey’s data science team converted the information for each product in the client’s inventory into numerical representations, known as embeddings, and saved them in a vector store. Embeddings convert human-readable data into mathematical representations that capture the meaning of the data. Then we followed these steps;

  1. Named Entity Recognition: The bot would identify the vehicle make, vehicle model, and the part name from the user’s initial query. If any of these necessary pieces of information were not provided, the bot would request it from the user.
  2. Vector Similarity Search: The embeddings for the provided information would then be calculated and matched against the product embeddings to reorder the whole product list by relevance.
  3. Retrieval-Augmented Generation: The most relevant products would be shortlisted and provided to the bot, which would then use this information to return a response to the user. 

Our proof of concept demonstrated that it is indeed possible to improve product search experience vastly with Gen AI. The initial approach we tried incurs significant cost for the LLM usage. We are now trying several different approaches to make it more cost effective. 

AI for Personalization at Scale

Personalization is becoming a critical factor for business success. According to Gartner, by 2026, 30% of new applications will use AI to drive personalized adaptive user interfaces, up from less than 5% today. 

A long standing client – Fresh Fitness Food (FFF), a London-based startup, provides personalized meal plans tailored to each customer’s macronutrient requirements. They cook, pack and deliver over 80,000 meals in London monthly, each with individualized portions. The complexity of balancing proteins, carbohydrates, and fats in each meal while providing meal variety is further compounded by having to account for allergies and meal preferences. All of this has to be done while maximizing economies of scale for FFF. 

Given our experience with this extreme personalization problem, our data science team recently tried to create a generic solution for such a complex personalization scenario. We started off by identifying that this is technically an optimization problem. With this realization we developed a solution as follows:

  1. Data Collection: We gathered detailed nutritional information for all possible meal components, as well as constraints imposed by recipe requirements. 
  2. Optimization Problem Definition: We defined the variables (e.g. quantities in grams of grilled chicken breast, boiled broccoli, and brown rice), constraints (e.g. quantity of brown rice should be at least 2 times the quantity of grilled chicken breast), and objectives (e.g. total protein, carbohydrate, and fat content should be 135 g, 296 g, and 63 g respectively) of the algorithm explicitly.
  3. Optimization Algorithm: We implemented an optimization algorithm that gets as close to the defined objectives as possible, given the variables and their constraints. We settled on using a Greedy Search Algorithm that provided a good balance between speed and accuracy. 

Details of our exact approach along with the supporting mathematical proofs are available here. 

Training an AI Bot to Answer Questions Based on a Set of Documents/Policies

In the realm of customer service and internal knowledge management, AI can revolutionize how information is accessed and utilized. Given that this is one of the most popular use cases for LLMs, we tried our hand at it with an internal product. We currently also serve a client, disrupting the tax industry with AI, that uses a similar prosperity tool to answer questions related to the US tax code. 

The challenge we gave our AI team was to develop a bot that could answer HR policy related questions from our employees that our internal HR team would periodically receive. The answers had to be based on our HR policies, all of which are documented. 

Here’s how we tackled this:

  1. Document Embeddings: We calculated the embeddings for the entire set of policy documents. Embeddings represent human-readable data as a set of numbers that capture its semantic meaning.
  2. Vector Similarity Search: When a user asked a question, the bot calculated its embeddings, compared it with those of the HR documents, and retrieved the most relevant information.
  3. Retrieval-Augmented Generation (RAG): The bot then used this retrieved information to generate a response to the user’s question. By referencing the policy document and the exact sections it was basing its answer on, the bot enables the user to verify the response and get further information. 
  4. Fine-tuning Prompts: We fine-tuned the LLM with appropriate prompts to only answer based explicitly on the policy documents fed to it (to minimize hallucination) and provide a standard “please contact the HR team” response for queries outside these boundaries. 

This approach allowed us to build a highly effective AI bot in quick time. Here are the exact steps we followed and the prompts we used.

Closing thoughts

At Calcey, we pride ourselves on our ability to solve complex business problems using the most appropriate technology. Get in touch, if you’re a business owner looking to collaborate with a team that can quickly grasp business problems and leverage the latest tools to swiftly deliver proof of value.

NewsTechTrends

We tried writing a software product spec with ChatGPT. Here’s our prompts and the results

Software product spec with GhatGPT

Being a Product Manager isn’t a walk in the park. You’re tasked with juggling prospects, budgets, stakeholders, dev teams, market analysis, competitor analysis, and much more. In some instances, you might even have a day job as an operational or technical leader. With all these hats on, it’s tough to break down business requirements in a way that your development team can easily grasp.

User Stories might seem straightforward, but writing them to cover all scenarios can be pretty daunting unless you’re a seasoned Business Analyst who practically lives and breathes User Stories.

Fortunately, we now live in the age of Generative AI., and with it, you can now generate most of the User Stories you need for a given requirement. While they might not be perfect, they’re certainly better than a vague one-liner requirement for a feature. With User Stories and Acceptance Criteria generated by Large Language Models (LLM), it only takes a few minutes to generate and tweak them to fit your needs.

For instance, I used a free ChatGPT (ChatGPT 3.5) account for this example. The scenario: building a platform where users can find experts to answer their questions and agree on a meeting time. ChatGPT can generate User Stories for the entire app in one go, but you get better control by generating them feature by feature. This way, you can fine-tune the content to be more precise.

If you look at the example below and the instructions we’ve given, you may get a slightly different response, but that is the beauty and nature of LLMs. This is what we got when we fed the following: 

Model instructions (Prompt)

Act as a world-class product owner and generate user stories, acceptance criteria for the following software feature

Response

Sure, please provide the details of the software feature you need user stories and acceptance criteria for.

Prompt

An onboarding module for experts and customers where experts would provide their area of expertise, years of experience and qualification (should be verified manually by one of our staff members) and customer onboarding should be quite simple

Response

Epic: Expert Onboarding

User Story 1: Expert Information Submission

  • As an expert,
  • I want to submit my area of expertise, years of experience, and qualifications,
  • so that I can be registered and verified on the platform.

Acceptance Criteria:

  1. The expert should be able to access the onboarding form.
  2. The form should include fields for area of expertise, years of experience, and qualifications.
  3. The form should allow the expert to upload relevant qualification documents.
  4. The expert should receive a confirmation notification upon successful submission.

User Story 2: Manual Verification by Staff

  • As a staff member,
  • I want to manually verify the qualifications submitted by the experts,
  • so that only qualified experts are approved on the platform.

Acceptance Criteria:

  1. The staff should have access to a dashboard listing all submitted expert applications.
  2. Each application should display the expert’s area of expertise, years of experience, and uploaded qualifications.
  3. Staff should have the ability to approve or reject an application.
  4. Experts should receive a notification regarding the status of their application (approved/rejected).

Epic: Customer Onboarding

User Story 3: Customer Registration

  • As a customer,
  • I want to register on the platform easily,
  • so that I can start using the services offered.

Acceptance Criteria:

  1. The customer should be able to access a simple registration form.
  2. The form should include basic fields: name, email, password, and phone number.
  3. The customer should receive a verification email upon registration.
  4. The customer should be redirected to the login page after successful registration.

User Story 4: Customer Login

  • As a customer,
  • I want to log in to my account,
  • so that I can access the services available to me.

Acceptance Criteria:

  1. The customer should be able to access the login form.
  2. The form should include fields for email and password.
  3. The customer should receive an error message if the credentials are incorrect.
  4. The customer should be redirected to their dashboard upon successful login

Prompt

A module where a user can search for an expert based on their field of expertise, experience level, ratings and mutually agree upon a time slot for a video conference

Response

Epic: Expert Search and Scheduling

User Story 1: Search for Experts

  • As a user,
  • I want to search for experts based on their field of expertise, experience level, and ratings,
  • so that I can find the most suitable expert for my needs.

Acceptance Criteria:

  1. The user should be able to access the search functionality.
  2. The search form should include filters for field of expertise, experience level, and ratings.
  3. The search results should display a list of experts matching the criteria with relevant details (name, field of expertise, experience, ratings).
  4. The user should be able to view detailed profiles of listed experts.

User Story 2: View Expert Profile

  • As a user,
  • I want to view the detailed profile of an expert,
  • so that I can assess their suitability before scheduling a consultation.

Acceptance Criteria:

  1. The user should be able to click on an expert’s name in the search results to view their profile.
  2. The expert’s profile should display their name, photo, field of expertise, years of experience, qualifications, ratings, and reviews.
  3. The user should see available time slots for video conferences on the expert’s profile page.

User Story 3: Schedule a Video Conference

  • As a user,
  • I want to schedule a video conference with an expert,
  • so that I can discuss my needs in a personalized session.

Acceptance Criteria:

  1. The user should be able to select an available time slot from the expert’s profile.
  2. The system should check for mutual availability between the user and the expert.
  3. The user should be able to confirm the booking after selecting a time slot.
  4. Both the user and the expert should receive a confirmation email with the details of the scheduled video conference.

User Story 4: Manage Scheduled Appointments

  • As a user or expert,
  • I want to view and manage my scheduled appointments,
  • so that I can keep track of and make any necessary changes to my consultations.

Acceptance Criteria:

  1. The user and expert should have access to a dashboard displaying all upcoming and past appointments.
  2. The user and expert should be able to reschedule or cancel an appointment.
  3. Notifications should be sent to both parties if an appointment is rescheduled or canceled.

Besides these, the LLM also generates detailed acceptance criteria, which I left out above.

If you look at the above example, there are some gaps in the requirements. For instance, customer onboarding appears to be acceptable. However, some basic fields such as Name, Email, and Password are missing in the expert onboarding.

Since the AI focuses on the specific modules you ask for, make sure to watch out for parts that mention features without much detail, and for potential inaccuracies. For instance, we talk about ratings, availability of experts, and an expert profile above, but we didn’t dive into how ratings will work, how experts mark/sync their available time slots or how experts can update their profiles. You can keep adding more features like rating,  and review systems, expert profile management, video conferencing, payment processing, and so on, just like we did with the first two features.

GenAI has come a long way since OpenAI kicked off the project in 2015. If you take a close look at the generated content above, you’ll notice plenty of spots where adding more details would make it better. So, don’t worry—AI isn’t going to steal your job as a Business Analyst. At least, not yet.

Calcey SpringboardEventsNews

One year to a new career in tech: Calcey Springboard concludes on a high

Calcey Springboard

Calcey, a boutique software development services provider catering to global clients from Sri Lanka, celebrated the graduation of the first batch of students from its groundbreaking Calcey Springboard program. This initiative equipped deserving students from non-technical backgrounds with the skills needed to build a career in the tech industry.

A learning experience which blends digital and in-person methods

What separated Calcey Springboard from traditional academic programs is that it not only provided each learner with a fully funded scholarship including devices (where necessary) and access to premium MOOCs from institutions such as Harvard University, GeorgiaTech and others, but also provided an in-person guided learning experience. Members of Calcey’s own engineering team stepped up to serve as mentors for the program and worked closely with learners to help them progress.

Nushrath, a graduate of the Springboard program, reflected on her time as a learner. “This is one of my greatest experiences, being here at Calcey learning technical and soft skills. I used to think that I’m not confident enough to work and be a part of an industry. But after enrolling in Calcey Springboard, my learnings through the curriculum and the mentors’ guidance helped identify my skills. Now, I’m confident enough to face an interview and be a part of the industry.”

“As a mentor, it’s been incredibly rewarding to witness the progress of these students over the last year. Watching them develop their skills and confidence has been truly inspiring and reaffirms the value of mentorship. For me, as a professional, working with these eager minds has been a refreshing and invigorating change of pace”, said Manujitha, a mentor and a senior software engineer at Calcey.

Quality, relevance, and academic rigor were also an inherent part of Springboard. Calcey’s own engineering leaders were involved in designing the curriculum and conducting exams for the students every semester.

Highlights from Springboard

A stepping stone to a better future

Calcey has welcomed eight of the graduates into their team (five are currently interning as engineers, while three are interning in quality assurance). Additionally, eight more graduates have secured internships and job opportunities elsewhere in the industry. A few remaining students have chosen to complete their tertiary education programs, and seek a job in the industry subsequently.

Mangala Karunaratne, the Co-Founder and CEO of Calcey, reflected on what the occasion meant to him.

“Setting up Calcey nearly two decades ago was essentially a bet we took on the quality of Sri Lankan engineering talent, and it paid off well. If you think of that as a withdrawal we made from the society around us and the universe or the world at large, Springboard is our way of making a deposit and giving back. We built a platform for hardworking individuals to change their destinies for the better, and it’s great to see these 23 individuals benefiting from it.”

“I would also like to extend appreciation for industry figures such as Pete Deemer, Harsha Purasinghe, Yudhanjaya Wijeratne, Anna Kalm, Bhanuka Harischandra, and Randhula De Silva, who helped us with the program and came forward to address the students by taking time off their busy schedules. Their contributions provided an extra element of variety to the program,” he added.

While celebrating its first batch of Springboard graduates, Calcey remains dedicated to investing in the future of Sri Lankan tech talent. The company also supports a range of other initiatives aimed at promoting tech education. In addition to a computer lab at the Maharagama YMBA, Calcey is a patron of an IT education program in Padaviya in the Anuradhapura District with 340 students.

Calcey Springboard

Highlights from the Springboard Graduation Ceremony

InsightsStartups

Mathematical Optimization in the Personalized Meal Delivery Industry

Mathematical Optimization

Optimization is defined as the action of making the best or most effective use of a resource. Optimization problems arise in various aspects of businesses:

  • Supply chain — optimal placement of inventory in warehouses to reduce transportation costs
  • Production — efficient allocation of tasks to employees to maximize their time and abilities
  • Delivery — shortest-path routing for last-mile delivery of products

Sometimes, optimization is a core component of the product itself. Such is the case in the personalized meal delivery industry, where each meal is tailored to meet a customer’s macronutrient requirements and delivered to their doorstep. At first glance this may seem like a straightforward case of increasing proteins, carbohydrates (carbs), and fats until a customer’s requirement is met. However in reality, proteins, carbs, and fats are not divorced from one another, and increasing one will disproportionately increase the other, making the task a lot more complicated.

Meal Balancing Problem

Let’s dig into it with a simple meal balancing example of a customer eating just one meal a day consisting of grilled chicken breast, boiled broccoli, and brown rice. The macronutrient information for each component along with the daily macronutrient requirement for a customer is given in Table 1.

ComponentCaloriesProteinsCarbsFats
Grilled Chicken Breast (1 g)1.92 kcal0.30 g0.00 g0.08 g
Boiled Broccoli (1 g)0.45 kcal0.02 g0.07 g0.01 g
Brown Rice (1 g)1.13 kcal0.02 g0.24 g0.01 g
Requirement (daily)2,291.00 kcal135.00 g296.00 g63.00 g

Table 1: The macronutrient information per gram for each component of a simple meal and a customer’s daily requirement.

As you can see, each meal component contains a mix of proteins, carbs, and fats, rather than just a single macronutrient. Meal components containing only a single macronutrient is very much a rarity. As such, a naive approach where each macronutrient is considered in isolation would not be viable. For example: increasing the quantity of grilled chicken breast to meet the fat requirement will disproportionately increase the quantity of protein and overshoot the protein requirement.

Analytical Solution

At this point you might dust off your 8th-grade math textbook, recognizing that this is essentially a system of linear equations, like so:

1.92𝑥 + 0.45𝑦 + 1.13𝑧 =
0.30𝑥 + 0.02𝑦 + 0.02𝑧 =
0.07𝑦 + 0.24𝑧 =
0.08𝑥 + 0.01𝑦 + 0.01𝑧 =
2291 Calories (1)
135 Proteins (2)
296 Carbs (3)
63 Fats (4)

𝑥, 𝑦, and 𝑧 represent grams of grilled chicken breast, boiled broccoli, and brown rice respectively. The calories equation (1)  is a linear combination of the other three equations, congruent with the standard calories per macronutrient, and can therefore be discarded:

Calories = 4 × Proteins + 4 × Carbs + 9 × Fats

This leaves us with 3 variables, and 3 equations, which means that the system may have an analytical solution. We can solve this via the substitution method by first expressing 𝑦 in terms of 𝑧 using the carbs equation (3):

0.07𝑦 + 0.24𝑧 =
7𝑦/100 + 24𝑧/100 =
𝑦 =
296
296
29600/7 – 24𝑧/7

Next, we can substitute for 𝑦 in the proteins equation (2) to express 𝑥 in terms of 𝑧:

0.30𝑥 + 0.02𝑦 + 0.02𝑧 =
3𝑥/10 + 2𝑦/100 + 2𝑧/100 =
3𝑥/10 + (2/100) × (29600/7 – 24𝑧/7) + 2𝑧/100 =
𝑥 =
135
135
135
3530/21 + 17𝑧/105

Then, we can substitute for both 𝑥 and 𝑦 in the fats equation (4) to solve for 𝑧:

0.08𝑥 + 0.01𝑦 + 0.01𝑧 =
8𝑥/100 + 𝑦/100 + 𝑧/100 =
8/100 × (3530/21 + 17𝑧/105) + (1/100) × (29600/7 – 24𝑧/7) + 𝑧/100 =
𝑧 =
63
63
63
-10900/17

Finally, we can substitute for 𝑧 in our previously derived expressions to solve for 𝑥 and 𝑦:

𝑥 = 3530/21 + 17𝑧/105 = 3530/21 + (17/105) × (-10900/17) =      450/7
𝑦 = 29600/7 –   24𝑧/7 = 29600/7 –   (24/7) × (-10900/17) = 764800/119

Which yields the following unique solution:

𝑥 = 450/7 ≈   64
𝑦 = 764800/119 ≈ 6427
𝑧 = -10900/17 ≈ -641

This is approximately 64 g of grilled chicken breast, 6,427 g of boiled broccoli, and -641 g of brown rice. If you crunch the numbers, this yields exactly 2,291 kcal, 135 g of proteins, 296 g of carbs, and 63 g of fats. However, this is not a plausible solution — the quantity of a meal component cannot be negative. Given that the system of equations yielded just one solution, our only option is to look for the next best solution given our constraints, i.e. the optimal solution.

Optimization Problem Definition

Now that we have correctly identified the meal balancing problem as an optimization problem, it is imperative that we formalize it. Let’s begin with a definition of optimization in the context of mathematics — Mathematical optimization is the selection of the best possible values for a set of decision variables, subject to constraints, that maximize or minimize an objective function.

In the case of our meal balancing example, we need to make a decision on the quantities of each meal component in order to achieve our goal. As such, the quantity in grams of grilled chicken breast, boiled broccoli, and brown rice, represented by 𝑥, 𝑦, and 𝑧 respectively, will be our decision variables. 

Additionally, there are also some real-world constraints that need to be considered for each meal component: their quantity cannot be negative (simply impossible), and their quantity can only be incremented or decremented by a minimum amount (it is impractical to increase a portion of grilled chicken breast by say 0.1 g). For the sake of simplicity, let’s set the minimum increment/decrement amount to 10 g. Therefore, the decision variables are subject to the following constraints:

𝑥 > 0, 𝑥 ∈ {10, 20, 30, …}
𝑦 > 0, 𝑦 ∈ {10, 20, 30, …}
𝑧 > 0, 𝑧 ∈ {10, 20, 30, …}

Our objective is to meet the customer’s macronutrient requirements. In mathematical terms, this means minimizing the difference between the total of each macronutrient in the meal and its corresponding requirement, resulting in 3 objective functions:

𝑚𝑖𝑛𝑖𝑚𝑖𝑧𝑒    |𝑃𝑡 – 𝑃𝑟|
𝑚𝑖𝑛𝑖𝑚𝑖𝑧𝑒    |𝐶𝑡 – 𝐶𝑟|
𝑚𝑖𝑛𝑖𝑚𝑖𝑧𝑒    |𝐹𝑡 – 𝐹𝑟|

𝑃, 𝐶, and 𝐹 represent proteins, carbs, and fats respectively, and subscript 𝑡 and 𝑟 denote the total and required quantities. These 3 objective functions can be collapsed into a single objective function by assigning weights based on their relative importance and summing them up. Their contribution to calories can serve as an appropriate weight:

𝑚𝑖𝑛𝑖𝑚𝑖𝑧𝑒    4×|𝑃𝑡-𝑃𝑟| + 4×|𝐶𝑡-𝐶𝑟| + 9×|𝐹𝑡-𝐹𝑟|

Using Table 1, we can express the total and required quantities in terms of the decision variables 𝑥, 𝑦, and 𝑧:

𝑃𝑡 = 0.30𝑥 + 0.02𝑦 + 0.02𝑧, 𝑃𝑟 = 135
𝐶𝑡 = 0.07𝑦 + 0.24𝑧, 𝐶𝑟 = 296
𝐹𝑡 = 0.08𝑥 + 0.01𝑦 + 0.01𝑧, 𝐹𝑟 =  63

Substituting for these variables in our objective function yields:

𝑚𝑖𝑛𝑖𝑚𝑖𝑧𝑒    |1.20𝑥 + 0.08𝑦 + 0.08𝑧 – 540| + |0.28𝑦 + 0.96𝑧 – 1184| + |0.72𝑥 + 0.09𝑦 + 0.09𝑧 – 567|

Putting it all together, our meal balancing problem can be formalized as:

𝑔𝑖𝑣𝑒𝑛𝑥, 𝑦, and 𝑧
𝑚𝑖𝑛𝑖𝑚𝑖𝑧𝑒|1.20𝑥 + 0.08𝑦 + 0.08𝑧 – 540| + |0.28𝑦 + 0.96𝑧 – 1184| + |0.72𝑥 + 0.09𝑦 + 0.09𝑧 – 567|
𝑠𝑢𝑏𝑗𝑒𝑐𝑡 𝑡𝑜𝑥 > 0, 𝑥 ∈ {10, 20, 30, …}
𝑦 > 0, 𝑦 ∈ {10, 20, 30, …}
𝑧 > 0, 𝑧 ∈ {10, 20, 30, …}

Trial and Error

If we were to tackle this problem by hand, we would typically employ a trial and error approach. We would pick arbitrary starting values for the decision variables, evaluate the objective function, intelligently increment or decrement each decision variable accordingly, evaluate the objective function, intelligently increment or decrement each decision variable accordingly, evaluate the objective function, and so on. This approach is illustrated in Table 2.

Trial𝑥𝑦𝑧CaloriesProteinsCarbsFatsObjective
0100100100350 kcal34 g31 g10 g1,941
15001001001,118 kcal154 g31 g42 g1,325
24001001,0001,943 kcal142 g247 g43 g404
33505001,0002,027 kcal135 g275 g43 g264
43509001,0002,207 kcal143 g303 g47 g204
53508001,0002,162 kcal141 g296 g46 g177

Table 2: The trial and error method for finding the optimal solution, recorded for 5 iterations.

After 5 iterations, we have reached a fairly acceptable solution of 350 g of grilled chicken breast, 800 g of boiled broccoli, and 1,000 g of brown rice. The comparison with the customer’s macronutrient requirement is shown in Table 3.

CaloriesProteinsCarbsFatsObjectiveRuntime
Requirement2,291.0 kcal135.0 g296.0 g63.0 g0.0
Trial and Error2,218.0 kcal141.0 g296.0 g46.0 g177.0~5 mins

Table 3: The evaluation of the optimal solution produced by the trial and error approach.

However, this approach can be quite cumbersome — imagine repeating this process for thousands of customers! Furthermore, we do not know whether this is the optimal solution to the problem. To overcome this, we need to enlist the aid of a computer.

Brute Force Search

What we have done by hand in the trial and error approach is called a search algorithm. In computing, one of the simplest search algorithms is the brute force algorithm. In contrast to the trial and error approach, where only a small subset of the potential solutions are evaluated, the brute force algorithm systematically enumerates all possible combinations of values of the decision variables and evaluates the objective function for every single one. This guarantees that the most optimal solution to the problem is found.

Continuing with our meal balancing example, we would have to set an upper bound for each meal component so that the search algorithm does not run indefinitely. Let’s set this upper bound to 10,000 g for each component:

0 < 𝑥 ≤ 10,000, 𝑥 ∈ {10, 20, 30, …, 10,000}
0 < 𝑦 ≤ 10,000, 𝑦 ∈ {10, 20, 30, …, 10,000}
0 < 𝑧 ≤ 10,000, 𝑧 ∈ {10, 20, 30, …, 10,000}

The code snippet in Figure 1 implements this brute force algorithm in Python.

x_all = y_all = z_all = [i*10 for i in range(1, 1001)]
x_opt = y_opt = z_opt = 0
obj_opt = float('inf')

for x in x_all:
    for y in y_all:
        for z in z_all:            
            prot_diff = abs(0.30*x + 0.02*y + 0.02*z - 135)
            carb_diff = abs(0.07*y + 0.24*z - 296)
            fat_diff = abs(0.08*x + 0.01*y + 0.01*z - 63)
            obj = 4*prot_diff + 4*carb_diff + 9*fat_diff
            
            if obj < obj_opt:
                obj_opt = obj
                x_opt = x
                y_opt = y
                z_opt = z
        
print('x =', x_opt)
print('y =', y_opt)
print('z =', z_opt)

Figure 1: The Python code snippet that implements the brute force algorithm.

After running for ~37 minutes, the algorithm produced the following optimal solution:

𝑥 =   170
𝑦 = 4,190
𝑧 =    10

Which is 170 g of grilled chicken breast, 4,190 g of boiled broccoli, and 10 g of brown rice, achieving an objective function value of just 68. Admittedly, this is a very odd meal composition, but for the sake of keeping things simple, we did not introduce any additional constraints. The comparison with the customer’s macronutrient requirement as well as the solution produced by the trial and error approach is shown in Table 4.

CaloriesProteinsCarbsFatsObjectiveRuntime
Requirement2,291.0 kcal135.0 g296.0 g63.0 g0.0
Trial and Error2,218.0 kcal141.0 g296.0 g46.0 g177.0~5 mins
Brute Force2,223.2 kcal135.0 g295.7 g55.6 g68.0~37 mins

Table 4: The evaluation of the optimal solution produced by the brute force approach.

The solution of the brute force approach is clearly superior to that of the trial and error approach. However, we should note that the algorithm took much longer to run. This is because the decision variables can each be one of 1,000 discrete values, which means that the brute force algorithm had to evaluate 1,000 x 1,000 x 1,000 = 1,000,000,000 (1 billion) potential solutions!

Consider the effect of introducing a lemon and herb sauce as a fourth component to the meal: assuming it can also be one of 1,000 discrete values, the brute force algorithm would now have to evaluate 1,000 x 1,000 x 1,000 x 1,000 = 1,000,000,000,000 (1 trillion) potential solutions! This is an exponential increase in the required computational power, and adding a second or third meal into the mix will further escalate this number. This is known as combinatorial explosion, and is the primary disadvantage of the brute force algorithm.

Greedy Search

To solve this issue of combinatorial explosion, we need to devise a more intelligent algorithm for selecting which potential solutions to evaluate, as opposed to systematically evaluating all of them. Essentially, we need to formalize the thought process that we used during the trial and error approach when deciding how to increment or decrement each decision variable following an evaluation of the objective function, i.e. which potential solution to evaluate next. In fact, quite a few such algorithms exist and are broadly summarized in Figure 2.

These algorithms try to find the absolute best solution to a problem by meticulously searching through every possibility to ensure finding the perfect solution. Some examples include:

  • Brute Force: Trying every solution one by one to find the optimal solution.
  • Dynamic Programming: Breaking a big problem into smaller parts, and storing solutions to avoid redundant computations.
  • Branch and Bound: Systematically dividing the solution space into smaller subspaces to find the optimal solution.

These algorithms make quick yet suboptimal decisions, with the aim of finding a pretty good solution quickly, even if it is not perfect. Some examples include:

  • Greedy Algorithms: Making the best decision at each step without worrying too much about future consequences.
  • Heuristic Algorithms: Using rules of thumb or intuition to make decisions that are usually good but not guaranteed to be the best.
  • Metaheuristic Algorithms: High-level strategies that guide the search for solutions in large or complex optimization problems.

These are specialized algorithms tailored to specific types of problems. Some examples include:

  • Linear Programming: Solving problems with linear equations and inequalities to find the best outcome.
  • Integer Programming: Dealing with problems where variables have to be whole numbers, not just any numbers.
  • Network Flow Algorithms: Optimizing the flow of goods, information, or resources through interconnected nodes and edges in a network.

Figure 2: A brief overview of the algorithms used to solve optimization problems, categorized into three archetypes.

For the sake of brevity, let’s discuss just one of the most commonly used algorithms — the greedy algorithm. It is best illustrated with the use of a metaphor:

You are a mountain climber attempting to find the shortest path to reach the summit of a mountain. With no map or GPS to guide you, you look at the terrain immediately surrounding you to identify which direction leads you to a higher elevation the fastest, i.e. the direction with the steepest upward slope. You take a step in that direction, pause and reassess your immediate surroundings to identify the new direction of the steepest upward slope. You take another step in this new direction, and repeat this process until eventually you reach a point where there are only downward slopes in all directions. Congratulations, you have now reached the summit!

This is the essence of the greedy algorithm. The objective function is the difference in elevation between yourself and the summit, which needs to be minimized. The decision variables are which path you should take, constrained by the surface of the mountain. At each point you only consider the effect of your immediate next step on the objective function, with no concern for the steps that follow, which is why it is called the greedy algorithm.

However, following this algorithm, you are not guaranteed to find the optimal solution. Continuing with our metaphor:

Proud of your accomplishment, you look around to soak in the beautiful view, only to notice the true summit looming in the distance. Your algorithm has failed you, and you have instead arrived at a false summit!

This is called a local optimum. Whilst a local optimum is a decent solution to the problem, it is not as good as the global optimum. Greedy algorithms in particular are susceptible to getting stuck in local optimums because they only consider the immediate effect on the objective function, without considering long-term consequences. 

However, this only occurs for nonlinear problems. Our mountain climbing metaphor is nonlinear because mountainous terrain peaks and valleys at various points. If instead the climber was climbing a pyramid, which only has a single peak, the greedy algorithm would always lead him to the global, and only, optimum.

Thankfully, our meal balancing problem is linear, so we can return to that and apply the greedy algorithm to get an optimal solution. This is implemented in the code snippet in Figure 2.

import pulp as pl

model = pl.LpProblem('blog', pl.LpMinimize)

x = pl.LpVariable('x', 1, 1000, pl.LpInteger)
y = pl.LpVariable('y', 1, 1000, pl.LpInteger)
z = pl.LpVariable('z', 1, 1000, pl.LpInteger)
prot_diff = pl.LpVariable('prot_diff', 0, None, pl.LpContinuous)
carb_diff = pl.LpVariable('carb_diff', 0, None, pl.LpContinuous)
fat_diff = pl.LpVariable('fat_diff', 0, None, pl.LpContinuous)

model += prot_diff >= (0.30*x + 0.02*y + 0.02*z)*10 - 135
model += prot_diff >= 135 - (0.30*x + 0.02*y + 0.02*z)*10
model += carb_diff >= (0.07*y + 0.24*z)*10 - 296
model += carb_diff >= 296 - (0.07*y + 0.24*z)*10
model += fat_diff >= (0.08*x + 0.01*y + 0.01*z)*10 - 63
model += fat_diff >= 63 - (0.08*x + 0.01*y + 0.01*z)*10

model += 4*prot_diff + 4*carb_diff + 9*fat_diff

model.solve()

for v in model.variables():
    if v.name in ['x', 'y', 'z']:
        print(v.name, '=', v.varValue*10)

Figure 2: The Python code snippet that implements the greedy algorithm with the help of the PuLP library.

Running the code snippet produces exactly the same solution as that of the brute force algorithm:

𝑥 =   170
𝑦 = 4,190
𝑧 =    10

Except this time, the solution was produced almost instantaneously instead of after ~37 minutes. The comparison with the customer’s macronutrient requirement as well as the solutions produced by the prior approaches is shown in Table 5.

CaloriesProteinsCarbsFatsObjectiveRuntime
Requirement2,291.0 kcal135.0 g296.0 g63.0 g0.0
Trial and Error2,218.0 kcal141.0 g296.0 g46.0 g177.0~5 mins
Brute Force2,223.2 kcal135.0 g295.7 g55.6 g68.0~37 mins
Greedy2,223.2 kcal135.0 g295.7 g55.6 g68.0≪1 secs

Table 5: The evaluation of the optimal solution produced by the greedy approach.

Conclusion

Optimization plays a pivotal role in any business, especially in the personalized meal delivery industry where it is integral to the product itself. In the short term, a manual trial and error approach will offer a reasonable solution. However, as the business grows this approach becomes unsustainable, and a more methodical search algorithm should be considered. Fortunately, there exists an abundance of such algorithms with corresponding programming libraries that implement them. Selecting a suitable search algorithm for a particular use-case can result in substantial improvements in both accuracy and speed when compared to a manual trial and error approach.

EventsLife at Calcey

“(sh)it happens” – A journey of learning, from slip-ups to success

Calcey shit happens

Ever made a work blunder and wished for a do-over? Join the club – you’re not alone. We’ve all been there, learnt our lessons, maybe even scored a T-shirt along the way. That’s why our company introduced a tradition we affectionately call “(sh)it happens!” Inspired by the regular  Mortality and Morbidity meetings practiced in healthcare settings, our tradition mirrors how surgeons extract lessons from clinical outcomes, review team performance, and drive  improvements in service delivery.

“(sh)it happens” is our way of breaking free from the fear of failure that tends to hold us back in the Asian work culture. Each edition starts with two individuals stepping into the spotlight, sharing their memorable slip-ups. Far from a blame game, the focus is on embracing vulnerability, and sharing wisdom from our mistakes. It’s about creating a safe space to discuss what went wrong and how we can improve – a departure from the typical cover-your-back mentality.

Highlights from our (sh)it happens editions

The success of each edition lies in the active engagement and participation of all employees. That is why we’ve introduced a delightful twist – a post-(sh)it happens karaoke session, complete with eats and drinks. The laid-back setting provides everyone with the chance to unwind, connect, and foster one-on-one relationships. This is key to helping people become more open and transparent – essential for making our hybrid work model effective.

Karaoke sessions at (sh)it happens

So why do we organize these gatherings? Well, beyond the obvious fun factor, it’s part of our effort to bring a touch of Silicon Valley spirit into our culture. In the Valley, failure is not feared; it’s seen as a stepping stone to success. “(sh)it happens” aims to instill this tolerance for failure, promoting a culture where we openly discuss mistakes and encourage employees to learn from each other to avoid repeating mistakes we’ve made in one project elsewhere.

Let’s be real – we learn little from success. Failure is a much better teacher. The true essence lies in learning from our mistakes and moving forward collectively. So, here’s to the unglamorous blunders, the lessons learnt the hard way, and the shared laughter in our work lives. Cheers to “(sh)it happens” – where learning from failure isn’t just accepted; it’s actively encouraged.

FintechTech

Giving Voice to the Unbanked

DreamStart Labs

DreamStart Labs (DSL) is a fintech with a social mission. Their main product, ‘DreamSave’, is a free app designed to simplify the management of savings groups in unbanked communities of developing countries. These individuals often find themselves excluded from conventional credit systems due to their inability to meet traditional creditworthiness standards.

The DreamSave application bridges this gap by helping users conduct meetings, handle financial records, reach savings targets, and establish credit histories. With every new user, DreamSave moves one step closer towards its goal of eradicating poverty and empowering community banks and savings groups.

The Initial Version of DreamSave

The first version of DreamSave was created as a Minimum Viable Product (MVP) to explore this concept of digitizing savings groups. The MVP was beta tested in four South African countries to validate the product offering. During this phase, the DreamSave engineering team identified the MVP’s limitations and bottlenecks. They also pinpointed specific areas that needed improvement to provide users with more features and benefits. The beta phase was a success as the application quickly garnered significant traction within these communities. The ability to no longer be tied down to paper trails and physically manage your savings group was groundbreaking and adoption was quick towards the DreamSave app.

Figure 1: DreamSave MVP High-Level Architecture

As shown in the diagram above, the DreamSave backend is a monolithic application developed in Java Spring Boot. It offers a JSON-based REST API that fuels the DreamSave mobile application. All incoming API requests are handled synchronously, and internal data processing is handled asynchronously using separate threads and stored in a single database.

This architecture facilitates vertical scaling, but given our ambitious expansion goals, we recognized the need to ensure that DreamSave 2.0 could also scale horizontally, enabling us to efficiently manage demanding workloads in a distributed manner.

Dream Save 2.0

At this juncture, it became evident that the concept of digitizing community-based savings groups had garnered significant traction. With the tremendous success of DreamSave 1.0, a multitude of strategic partnerships with NGOs were on the table and plans were in place to extend DreamSave’s reach from four to twenty different counties to serve more users.

The MVP proved that the idea was valid and it was now the time to scale the product to greater heights. DreamStart Labs acknowledged that the initial MVP was insufficient for this expanded scale of operations and carried technical debt. Recognizing the need for a capable software development partner, DSL sought to build a more robust version of their product to take it to market. This led to a collaboration with Calcey, where the decision was made to completely rebuild the DreamSave Platform. The primary focus was on enhancing security, scalability, and high availability, crucial elements to support their ambitious expansion plans.

Design Goals of DreamSave 2.0

The team learned a great deal from DreamSave 1.0. They then embarked on a ground-up re-engineering of the solution, focusing on adhering to the highest architectural standards. After many sit downs with the client and our internal teams, the following key functional and non-functional requirements were identified as key to the larger solution. 

  • Offline Capability: DreamSave primarily serves users in remote areas with limited connectivity and expensive mobile data. To address these challenges, DreamSave 2.0 is designed to function seamlessly without an active internet connection. It automatically syncs data to the cloud in the background when a connection is available, ensuring uninterrupted usage even offline.
  • Traceability and Compliance: In a data-driven FinTech product like DreamSave, understanding the reasons behind data changes is vital. Instead of storing only the final data state, DreamSave 2.0 maintains a comprehensive log of all events or actions that lead to data modifications. This approach enables detailed ‘time-travel’ through data, particularly valuable for advanced use cases like auditing.
  • Reduced Mobile Data Usage: Mobile data costs are significant in our target markets. DreamSave 2.0 focuses on minimizing mobile data consumption, aiming for a 60% reduction on data sync compared to the MVP version to ease the financial burden on users.
  • Faster Data Synchronization: Our beta testing revealed that over 90% of users operate the application offline, with short online intervals. To maximize these online moments, we aim to significantly enhance data synchronization speed, targeting a 40% improvement over the MVP version on a standard 4G mobile data connection.
  • Near-Real-Time Data Processing: Once data is synchronized to the cloud, the data is readily available to be viewed by its members. Additionally, computationally intensive operations are performed to generate advanced business analytics. DreamSave 2.0 ensures seamless scalability, offering near-real-time access to these analytics on our dashboards, irrespective of data volume.
  • Backward Compatibility:  Despite a new data model, DreamSave 2.0 is committed to migrating existing data to the new version. We made it easy for all existing users to transition to DreamSave 2.0, enabling them to retain their data with a single button click.

By addressing these requirements with empathy towards our target audience, we ensured that DreamSave 2.0 was geared for success.

Design and Technology Selection Process

Optimizing HTTP Request Payload Size

In designing DreamSave 2.0, our primary objective was to ensure full functionality even in the absence of an internet connection. The model achieves this by locally storing data on the device until an active internet connection becomes available for seamless data transfer to the cloud. During the initial beta testing phase, it became apparent that a savings group comprising 30 members required approximately 12 to 13 megabytes to upload a single meeting to the cloud. With the average group conducting four meetings per month, this translated to an accumulation of approximately 50+ megabytes. These findings raised concerns, particularly in consideration of our user base and the mobile data landscape in our target markets. Consequently, the engineering team recognized the need to optimize communication between DreamSave 2.0 application and the backend servers.

After a thorough investigation, the team identified the following areas for improvement:

  • Payload Structure Optimization: In the MVP version of DreamSave, a JSON-based REST API powered the mobile application. It was evident that the HTTP request payloads could be further simplified by eliminating redundant information to reduce the overall payload size. However, the benefits of this optimization did not meet our expectations.
  • Adoption of Alternative Data Formats: As an alternative to the JSON format used in the MVP, the team conducted an evaluation of other options and identified Google Protocol Buffers as an ideal replacement for JSON. Protocol Buffers, developed by Google, provide a language-neutral, platform-neutral, and extensible method for serializing structured data. After several experiments, it became clear that protobuf messages were approximately 30% smaller than JSON, as they use a binary format for data serialization.

Beyond the efficiency and compactness of the Protobuf format, the team considered its support for a broader range of data types and the utility of schema enforcement in creating a future-proof and maintainable product. This played a pivotal role in the decision to implement Protobuf in DreamSave 2.0.

DreamSave 2.0

Figure 2: Utilizing Protocol Buffers in DreamSave 2.0

As depicted in the diagram above, DreamSave 2.0 utilizes a REST API to enable communication between the backend servers and the mobile application. The data payloads for the HTTP requests are serialized using Protocol Buffers.

Preserving Data Histories with Event Sourcing

During the design phase of DreamSave 2.0’s architecture, the engineering team intentionally chose to build it in a way that would support the preservation of the business context or the narrative behind each data point or state change within the system. For instance, when a customer’s name changes from “John” to “Mark”, it’s not enough to merely store “Mark”. The system should retain the knowledge that this name transitioned from “John” to “Mark” at any point in the future. This requires going beyond storing only the current state of the data. To fulfill this requirement, the team opted to incorporate the Event Sourcing pattern into DreamSave 2.0. The Event Sourcing pattern defines an approach to managing data operations driven by a sequence of events, each of which is logged in an append-only event store. The application code generates a series of events that explicitly describe each action taken on the data and records these events in the event store. In practical terms, when a data change occurs in a domain, it’s unnecessary to transmit the entire domain entity across the network. Instead, only the specific change is transmitted as an individual event, significantly reducing mobile data usage by minimizing the request payload size.

Furthermore, the adoption of the Event Sourcing model offers additional advantages that can prove invaluable in advanced use cases:

  • Time Travel through Data: It enables the ability to traverse event streams in any direction for advanced analysis.
  • Drive Business Intelligence: The system retains the business context, allowing for deeper insights.
  • Improved Data Integrity and Auditability: Thanks to the immutable nature of Event Sourcing, data remains complete, secure, and readily auditable.

Given these advantages and benefits, the team made a deliberate choice to adapt the Event Sourcing pattern in DreamSave 2.0.

Achieving High Throughput and Low Latency with Backend APIs

A fundamental role of DreamSave 2.0’s backend is to facilitate the seamless synchronization of data with the cloud via its APIs. Considering the application’s offline-first approach, our engineering team understood the critical importance of prioritizing high throughput for write operations over read operations within the backend API. To ensure independent scalability, high availability, and simplified maintenance, the team chose to incorporate the CQRS (Command and Query Responsibility Segregation) pattern in the architectural design of the DreamSave 2.0 backend.

Calcey

Figure 3: DreamSave 2.0 Backend Using Separate Read and Write APIs

The diagram above visually illustrates how the DreamSave 2.0 backend incorporates the CQRS pattern, employing distinct APIs for managing read and write operations. Additionally, the adoption of separate databases provides flexibility in utilizing specific database engines (whether NoSQL or SQL) while optimizing schemas, rather than adhering to a one-size-fits-all database engine or schema. This architectural approach promotes a loosely coupled system, enabling individual APIs to scale independently and efficiently handle high throughput.

The design decisions discussed above play a pivotal role in the development of high-throughput APIs. However, the team also recognizes that network latency is a distinct concern that warrants separate consideration, especially in the context of the deployment architecture. The final section of this article delves into this aspect, providing comprehensive insights into the overall architectural justification.

Real-Time Data Processing and Data Projections

As previously discussed in this article, the Event Sourcing approach revolves around managing data operations through a sequence of events, meticulously logged in an append-only event store. Retrieving the current state of a specific domain involves replaying all events associated with that domain from the beginning. While this approach offers numerous advantages, as outlined earlier in this article, there are specific use cases, particularly those involving advanced aggregate queries, where adopting a normalized, relational database is more practical.

In such scenarios, diverse data projections can be created based on the events stored in the event store. The creation of projections involves extracting the current state of a domain from an event stream. In the context of DreamSave, our team recognized the need for two primary projections:

  • Extended Event Log Projection from the Main Event Store: This projection encompasses all events from the primary event store and includes additional events generated by the system based on specific business logic. The DreamSave 2.0 application predominantly relies on this data model, as it uses the Event Sourcing model to synchronize data with the cloud.
  • Normalized Relational Read Model: This projection stores data in a normalized relational database, offering robust support for advanced queries. Data within this model consistently represents its current state at any given point, eliminating the need for replay operations when accessing data. This data model is used to share data with external entities through DreamSave Developer APIs.

As events are received by the backend, they are written into the Event Store. However, simply storing these events is insufficient—they must be processed in the exact same order to construct the projections. This is where the need for an event processing platform comes into play. After evaluating various options, the team opted to adapt Apache Kafka to manage event processing within the DreamSave 2.0 backend. Apache Kafka was selected for its proven stability and its support for distributed data processing.

Bringing It All Together

As the team set out to design DreamSave 2.0, they carefully considered the design objectives mentioned earlier. Following numerous iterations and in-depth design discussions, the team arrived at the following design:

DreamSave 2.0 Calcey

Figure 4: DreamSave 2.0 High-Level Backend Architecture

In the architectural diagram above, we illustrate how all DreamSave 2.0 APIs are exposed to the external world through an API gateway, complemented by a load balancer to efficiently distribute traffic across multiple backend instances. An essential feature of this setup is that the API strictly accepts Protocol Buffers, optimizing data exchange.

The backend of DreamSave 2.0 adheres to the CQRS pattern, treating the Read and Write aspects as distinct components. On the Write side, all incoming events are instantly written to an immutable EventStore, backed by MongoDB. Concurrently, these events are pushed to a single Kafka topic, initiating the processing and construction of the required data projections. Kafka guarantees the order of the events only within the same topic, and when it comes to Event Sourcing, the order of the events is crucial. This is why all events are published to a single topic with a specific partition key.

Data integrity is a central concern in our system. To support this, the team recognized that writing events to the Event Store and publishing events to Kafka should occur as an atomic operation. This is achieved through the execution of both operations within a single distributed transaction.

Another notable design element is the immutability of the Event Store, which simplifies event caching. With no need for cache invalidation, significant performance gains are realized. DreamSave 2.0’s backend leverages a distributed cache backed by Redis to further optimize operations.

Multiple Kafka consumer groups are subscribed to the single Kafka topic where all events are published. These consumer groups are responsible for processing incoming events and constructing the necessary data projections. In the context of the DreamSave 2.0 backend, two primary consumer groups stand out: the Profile Activity Consumer Group, tasked with building the Extended Event Log, and the Read Model Consumer Group, responsible for creating the Normalized, Relational data model. Both of these materialized views are written into a PostgreSQL relational database, ensuring optimized data retrieval for the read API.

To minimize network latency, the system can be deployed in multiple geographic locations, although this can be costly. Alternatively, CDNs can be utilized, but their performance shines best with static content. Our engineering team sought to reduce the latency of the sync API while keeping costs manageable. To achieve this, DreamSave 2.0’s backend relies on the Google Edge Network Point of Presence (PoP).

When a request is made to the DreamSave 2.0 API, the traffic flows directly from the user’s network and enters Google’s network at an edge Point of Presence (PoP) close to the user’s location. It then continues to use Google’s internal backbone to reach the virtual machine (VM) hosted on Compute Engine. This approach significantly reduces the network latency of the APIs by reducing the Time to First Byte (TTFB).

Recognition and Awards

Good product engineering is all about empathizing with how people actually go about their day-to-day lives. That is what DreamSave 2.0 went out to achieve and our efforts have been recognised. 

DreamSave’s impact on the world has resulted in a slew of awards including: 

In addition, DSL has partnered with AB Bank Zambia and BRAC Bangladesh to provide access to digital financial services and secure small business loans. 

While it feels great to be part of a venture which has grown steadily over the years (Since 2020, DreamSave has facilitated more than 2.4 million transactions in 21 countries), it’s even more heartwarming to see how our work is making a positive impact in the lives of many. And that feeling…is priceless💜.

To learn more about our work with DreamStart Labs read our case study: https://calcey.com/work/dreamstart-labs/

NewsTechTrends

HRGPT — a chatbot that answers questions related to human resource policies

ChatGPT for IT companies

Written by Viren Dias – Lead Data Scientist at Calcey

In recent months, ChatGPT and its uncanny ability to understand human language and mimic human responses had created quite the buzz around the Calcey office: it got us thinking about how we could incorporate it and its underlying technology into our products. We thought the best way to figure that out would be to trial it in an internal project, and for that, we needed an appropriate use case.

Our colleagues over at Human Resources (HR) had been spending their valuable time meticulously documenting HR policies in an internal wiki, only for us slackers to be too lazy to read it, and waste even more of their time by querying them directly! This sparked an idea — why not create a chatbot that could leverage the information contained within the HR wiki to answer questions related to HR policies?

What is ChatGPT?

ChatGPT is a large language model (LLM) trained to understand and respond to natural language conversations. As its name would suggest, it has been built on top of a Generative Pre-trained Transformer (GPT), more specifically its latest iteration, GPT-4. 

GPT is a family of LLMs, trained on curated portions of the internet and human feedback, in order to predict the next token in a given document. In simpler terms, given a prompt, the model will attempt to predict what the response should be, using the internet as its source of knowledge. In the case of ChatGPT, the prompt is simply an incomplete chat log, and the model will attempt to predict what the next message should be.

How we adapted ChatGPT to our task

Figure 1: A flowchart of the processes involved in producing a response to a user-supplied query regarding HR policies.

At a high level, the processes involved in adapting ChatGPT to respond to questions related to HR policies can be aggregated into three distinct stages:

  1. A preprocessing stage, where we convert the HR policy documents into a machine-readable format and break them down into congruent sections.
  2. A sorting stage, where we sort the aforementioned sections by their relevance to the query.
  3. A response stage, where we supply the ChatGPT API with the necessary information to evoke an appropriate response.

We briefly discuss each of these stages and their associated engineering challenges below.

Preprocessing the HR policies

The HR policy documents are available in the form of wiki pages, which ChatGPT does not respond well to. Consequently, we needed to convert them into a ChatGPT-friendly format, which we identified iteratively through experimentation. This involved tasks such as: 

  • Stripping HTML tags, 
  • Running optical character recognition (OCR) software on images and infographics,
  • Converting HTML tables into Markdown tables, 
  • Printing hyperlink URLs explicitly alongside their text,
  • Etc.
IT companies in Sri Lanka

Figure 2: A visual depiction of the sectioning methodology. Each rectangle represents a separate section.

Additionally, some documents can get quite large and touch on several different HR policies: we needed to break these down into more manageable, congruent sections. We found that the best way to do this was to make use of heading levels and break down each document into nested sections, with each section containing all the sections hierarchically below it.

Sorting HR Policies by Relevance

In the interest of computational efficiency and accuracy of responses, we needed to sort the HR policy sections by their relevance to the query. To do this, we made use of text embeddings: a numerical representation of a text. A simple example of this would be tallying the occurrences of each word in a text. The embeddings of two texts can be input into a similarity function to easily determine how similar they are mathematical.

Once we calculated the embeddings of the query and all the HR policy sections, we computed the pairwise similarity. We then sorted the sections by the computed similarity to yield a list of HR policy sections ordered by their relevance to the query.

Evoking an appropriate response

To elicit a desired response from ChatGPT, we need to supply it with three pieces of information:

  • The relevant HR policy sections to inform ChatGPT of the HR policies,
  • The user-supplied query, and accompanying chat history provide context for the query, and
  • A well-engineered prompt to instruct ChatGPT on how to respond using the above data.

The only thing missing is a good prompt. Engineering a good prompt is an iterative process that requires a lot of experimentation. It is not always as simple as providing instructions — ChatGPT has historically had an issue with hallucination, whereby the model incorrectly yet confidently responds to a question it does not know the answer to. However, with the right prompt, this issue can be solved.

The prompt we settled on was as follows:

“You are a helpful assistant working at Calcey. Answer the questions truthfully and explicitly, providing additional details when available. Use only the information contained within the wiki, and if the answer is not contained in the wiki, ask the user to contact HR.”

Q: What is the reward for introducing a senior software engineer?

A: The reward for introducing a Senior Software Engineer is an iPhone 13 128GB. Please note that certain conditions apply, such as the candidate must not have applied to Calcey before the referral email, and the referee will not join the interview process. The reward will be ready within one month from the date your friend starts working with Calcey. To refer someone, send their resumes to internal-referrals@calcey.com.

(a) An example of a response containing additional relevant details to the query.

Q: How long can I stay in the dorm for?

A: The Calcey Dormitory Facility Policy does not explicitly mention a maximum duration for an employee's stay. If you have specific questions about the duration of your stay, it's best to contact HR for further clarification.

(b) An example of how it responds to a query it should not know the answer to.

Figure 3: Examples of how the prompt influences ChatGPT’s response.

Breaking down the prompt and explaining what each sentence does:

  • “You are a helpful assistant working at Calcey”. This ensures that the model responds politely and from the perspective of a colleague.
  • “Answer the questions truthfully and explicitly, providing additional details when available”. Instructing the model to provide additional details results in the response including links to forms it refers to, contact details for people it refers to, etc. However, this increases the risk of the aforementioned hallucination issue.
  • “Use only the information contained within the wiki, and if the answer is not contained in the wiki, ask the user to contact HR”. This greatly curbs the hallucination issue and provides explicit instructions on how to respond to questions the model does not know the answer to.

Closing Thoughts

ChatGPT is a very powerful tool that can be molded to suit a variety of use cases. However, since its inner workings are not precisely understood and its responses are stochastic in nature, it can be tricky to instruct it to do exactly what you want it to. As a result, it can require quite a bit of experimentation to get the desired outcome.

TechTrends

React Native Tips: How to optimize assets for better performance

React Native Tips

Written by Samitha Nanayakkara – Associate Tech Lead at Calcey

React Native is a favorite of mobile app developers everywhere as it allows you to build multi-platform apps using a single codebase.

However, as the complexity of an app increases, so does its size. Unoptimized code and assets lead to larger apps which consume valuable storage space on the devices they’re installed. Larger apps also mean slower downloads, and it could lead to users abandoning the app altogether out of frustration.
That is why it is vital to optimize assets such as images and animations. Doing so can drastically reduce app size and improve overall performance.

A developer can

  • Optimize the size of images AND
  • Optimize the resolution of images

Optimizing the size of images

PNG images are popular with React Native app developers due to their efficient rendering (compared to SVG) and high quality (compared to JPEG). However, these images can sometimes negatively impact the app’s performance. To address this issue, developers can use ‘TinyPNG’ to optimize and compress their PNG images without sacrificing quality. TinyPNG is a web service that uses advanced lossless compression techniques to reduce the file size of PNG images.

For one of our projects at Calcey, we were able to reduce the size of the production app by 67.43MB solely by optimizing image size. 

For macOS users, a convenient tool called “TinyPNG4Mac” can be installed to optimize an entire folder of images quickly with a simple drag and drop. TinyPNG4Mac can be a helpful tool in streamlining the image optimization process and reducing the size of the PNG images used in a React Native app.

Optimizing the resolution of images

Services such as TinyPNG won’t be very effective if high-res images are being used. 

As app developers, we often include alternative images to provide the best experience to our users. As displays continue to become better, it’s easy to be tempted to include the highest-resolution images in our apps. However, this isn’t always necessary.

Let’s take Android devices as an example. Most modern phones have a pixel density of 400-500 DPI, with the Samsung S22 Ultra having a pixel density of approximately 500 DPI. When an app is about to display an image, the Android operating system automatically determines the device’s pixel density and serves the correct alternative image. In the case of the S22 Ultra with WQHD+ mode enabled, Android will render images specified for the xxxhdpi density.  For example, if the app is about to display ‘my-photo.png’, the OS will select the mdpi, hdpi or any other alternative version of the particular image based on the phone’s pixel density.

But what size should these xxxhdpi images be? According to the Android documentation, xxxhdpi images should be 4x the size of the base images. For example, if the base image size is 48 x 48 pixels (i.e.1x), the xxxhdpi image should be 192 x 192 pixels (Figure 1). It’s important to remember that designers typically work at a 1x scale because it’s regarded as best practice and makes easy design scaling possible.

  • 36 x 36 (0.75x) for low-density (ldpi)
  • 48 x 48 (1.0x baseline) for medium-density (mdpi)
  • 72 x 72 (1.5x) for high-density (hdpi)
  • 96 x 96 (2.0x) for extra-high-density (xhdpi)
  • 144 x 144 (3.0x) for extra-extra-high-density (xxhdpi)
  • 192 x 192 (4.0x) for extra-extra-extra-high-density (xxxhdpi)

React Native Tips

Figure 1: Relative sizes for images at different density sizes

So, it is not necessary to include ultra-high-resolution images.

Since this post focuses on React Native, you might wonder if there is a rule of thumb for what the resolution of any image you include should be. According to React Native 0.70 Documentation, it is sufficient to have 1x, 2x, and 3x scaled images for any mobile device. These scales are similar to the scaling factors used by iOS.

Including higher resolution images beyond 3x may not be of much use, as mobile devices will scale the images down to fit their displays. In fact, including higher-resolution images can increase the size of your app unnecessarily, as explained previously.

In creating a lightweight, high-performing app, it’s best to stick with the 1x, 2x, and 3x scaled images. This is especially true if you design your assets at 1x scale, and you can then use the 2x and 3x scales as needed.

In a project we worked on at Calcey, we reduced the size of the production app by an additional 20 MB by optimizing our images. Altogether, we reduced the production app size by a whopping 90 MB through image optimization alone. This demonstrates the significant impact image optimization can have on the size and performance of a React Native app.

Additionally, given below are a few other methods that can be used to arrive at a smaller app size.

For Animations: Use Lottie

React Native Tips

Lottie is a library for Android, iOS, and the Web. It allows developers to include animations in their apps with ease, and is based on the open-source Bodymovin extension for Adobe After Effects. Lottie uses JSON files to describe the animation data.

Lottie can be a good choice for mobile apps compared to GIF and other animation formats.

  • Small file size
    Lottie animations are typically minor compared to  GIF and other video formats. This can reduce the size of the app  and improve its performance.
  • High-quality animations
    Lottie animations are based on vector graphics, meaning they are scalable  to any size without a loss in quality. This can  especially be useful in creating animations with smooth, clean lines and curves.
  • Customizability
    Lottie allows developers to customize the animations they include in their apps. This is useful in creating animations that match the style and branding of the app.
  • Premium makes things even better
    Lottie’s Premium Service can be used to optimize Lottie files to obtain smaller animation files.

Analyze the JS Bundle

react-native-bundle-visualizer is a tool that can be used to visualize the bundle size of a React Native app. It provides a visual representation of the size of the app’s JavaScript, assets, and other dependencies, allowing developers to identify areas where they can optimize the size of the app.

React Native Tips

There are several reasons why developers might want to use react-native-bundle-visualizer:

  • Identify areas for optimization
    By visualizing the size of the app’s bundle, developers can quickly identify areas where they can optimize the size of the app. This can include optimizing images, removing unused code, and using code-splitting techniques.
  • Monitor changes to the bundle size
    react-native-bundle-visualizer can be used to monitor changes to the bundle size over time, allowing developers to track the impact of their optimization efforts.
  • Improve app performance
    Reducing the size of the app’s bundle can improve app performance, particularly on devices with lower specifications. Using react-native-bundle-visualizer, developers can identify and address problem areas contributing to a larger bundle size.

Analyze the Android APK file
The Android APK Analyzer is a tool that allows developers to analyze the contents of their Android app packages (APKs) and understand their size and composition. It’s included in Android Studio and can be used to examine the size of the app’s code, resources, assets and identify potential areas for optimization.

React Native Tips

Figure 2: Analysis of an APK

One of the main benefits of using the Android APK Analyzer is that it allows developers to identify and optimize the size of their apps. For example, developers can use the tool to identify resources that take up a large amount of space e.g. large images or unused code. They can then take steps to optimize these resources, such as by compressing images or removing unused code.

By following the tips and techniques outlined here, you’ll be well on your way to building lightweight, high-performing React Native apps users love. And remember, every little bit of optimization can make a difference – even reducing the size of your app by just a few megabytes can vastly improve the user experience and make your app stand out in a crowded marketplace. Happy coding!

AnnouncementsOpinion

Can the right person be guided to begin a career as a programmer in 12 months? Yes, I think so

IT Scholarships in Sri Lanka

In my experience, my Sri Lankan colleagues are some of the most reliable, responsible and dedicated professionals that I have ever worked with. 

If you value technical soundness, tenacity, and creativity, that is. For everything we say about how the modern education system educates creativity out of our children, I’ve found local talent to be some of the best when it comes to getting the job done. Good programmers know how to follow documentation and put things together, but great programmers look at what’s in front of them and figure out ingenious ways to build something totally new. The latter is the common thread which binds every great programmer the world has ever known. 

Tech – The great catalyst for Sri Lanka

While the country awaits a deal with the IMF, I believe tech can be a powerful tool through which the economy can stage a rapid recovery. Sri Lanka’s IT industry doubled between 2015 and 2020, both in terms of revenue and the size of the workforce. By 2030, the industry could very well generate over US $ 6 billion in revenue, if it plays its cards right. Also, consider this: The total investment to produce a graduate amounts to nearly LKR 1.6 million (i.e. the cost of a bachelor’s degree from a private institute). But, the value each IT professional can generate in a single calendar year alone is many multiples of that. Not many industries can boast of such great investment dynamics (Source).

IT Scholarships in Sri Lanka

Imagine how much better things could be if we could reduce the cost and time taken to produce a tech grad, so that they can at least have a foot in the door?

The two choices before us

Now, there are two ways to get things moving in the right direction. You can push for top-down change (i.e. at the policy level), or get things going at the grassroots level. The former is necessary though time-consuming, but the latter is very much doable. And if the results are good enough, pushing for policy-level change will be so much easier.

That is why as a company, we launched Calcey Springboard.

Springboard is a 12-month, fully-funded coding bootcamp we’ve put together to support talented students get into tech. Calcey’s own engineers and industry leaders will guide them while they learn from a globally recognized curriculum of MOOCs, and they will be tested each semester by us. Students will be able to attend weekly mentoring sessions, and will get the chance to meet and interact with accomplished tech professionals from a wide variety of backgrounds (and a lot of interesting stories). Once they graduate, they can seek out an internship at any company they like!

We chose to put together a curriculum consisting of MOOCs simply because they’re proven, recognised, and are of a high standard. We’ve vetted them personally and can vouch for their quality. Sometimes, we even use them to train our own people on emerging technologies.

Why are we doing this?

The way we see it, we are simply giving back to society and doing our part to widen the pool of tech talent in Sri Lanka. We owe a debt of gratitude to Sri Lanka’s public education system which produced the awesome people who built Calcey into what it is today, and this is our way of paying back our dues.

Now that you know the what, the why, and the how behind Springboard, I have one more favor to ask you. We want to make sure we can award as many scholarships as we possibly can, and would be really glad if you could spread the word. Applications close on November 30th, which means whoever wishes to apply has 15 more days to do so. 

Apply for a Springboard scholarship at https://calcey.com/springboard/ !

-Mangala

Mangala Karunaratne is the Founder and CEO of Calcey.

Events

Lessons from 20 years of product ownership

Product ownership

Pete Deemer needs no introduction among the Agile software community. A key member of the Scrum Alliance who served as its chairperson in 2017, Pete has over 25 years of experience in guiding product development teams. He is the lead author of ‘The Distributed Scrum Primer’, a guide to multi-location Scrum and ‘The Scrum Primer’, one of the most widely read introductions to Agile development. Pete shared some insights and lessons from 20 years of product ownership with us recently, and here are some key takeaways:

How to speed up a development team

Asking a development team to simply work harder and faster, while giving them words of encouragement does not have a lasting impact. As the pressure increases and the team works longer hours, although output may increase for a while, burnout sets in soon after. Recovering from this is extremely difficult – quality reduces drastically while output rarely goes back to the same level it was.

When a programmer rushes, they make more mistakes. When a tester rushes, they find fewer mistakes.

Smart product owners therefore should identify ways in which to increase output without the risk of burnout. They can do this using the Agile methodology ‘Lean’: improve efficiency by eliminating waste. A key part of this is to reduce impediments. Pete defines an impediment as ‘anything that gets in our way, interrupts us, distracts us, causes mistakes, creates rework, causes waste, hurts our motivation or stops us from being as good as we can be’.

Identifying any and all impediments that the team has or foresees as early as possible, and figuring out how to navigate them or reduce them is greatly beneficial to the overall productivity of the team. It allows output to increase and stay at a high level for longer periods. Teams work the same hours but at a more sustainable pace, and are therefore less exhausted as they encounter fewer hurdles. Additionally, the work the team produces is often of high quality in this situation. It’s important that product owners create an environment of openness for the team to express themselves and be honest about all impediments they have – even if certain impediments are caused by the product owners themselves!

How to handle new requirements or – half of what you think you want, you don’t need, and a lot of what you actually need, you haven’t thought of yet!

A fundamental reality of product ownership is not knowing exactly which direction a product is heading. It is therefore vital to be able to adjust and change the sails and change course without causing disruption or panic.

During a Scrum Kickoff, the product owner identifies the product goal and creates a product backlog that consists of features or stories – things of direct value to customers or users. These are known as product backlog items (PBIs). PBIs are then broken down to smaller features, and organized into sprints to work towards the final release. However, a product owner may think of a brilliant idea for a new feature that will greatly enhance the product halfway through product development. Simply adding this new feature in without making any adjustments to the existing PBIs will cause a delay and disrupt the process as the overall product backlog increases.

There are four key factors that come into play when thinking about products – scope, schedule, cost and quality. These are interlinked, and a change in one aspect often results in a change in one or more of the others. Eg: If the product owner tries to increase the scope but not adjust the schedule and cost, the quality may see a drop.
It is important to note that, if the product owner and team do not willingly change each factor to reflect changes in the others, the changes may occur in unplanned and unexpected ways. This will often have a negative impact on the overall progress of the product.

Product ownership - Calcey

Therefore, to make allowances for new PBIs, the best option is to find PBIs or features that are of lower value and replace those with the new features. The overall scope stays the same as it is being balanced, and the value of the product increases (as your new addition is likely of higher value than the PBIs that were removed).

Flexibility is key in good product ownership and Pete reiterates that ‘we have not had our best ideas yet. Our best ideas are still to come. And we need to be prepared to respond to that.’

You don’t pick when your good ideas come. They come when they’re ready to come.

How to structure a product backlog

To be able to remove and replace PBIs, it is important to realistically evaluate the true value of each. Sorting PBIs by priority and value allows room for any adjustments to be made – such as if the team finds they need more time to fix a high value feature, or the product owner needs to add a new feature halfway through. This also allows the team to make any adjustments based on user feedback from beta testing. Similarly, if due to some reason the release date has been reached but some PBIs are incomplete, it will have only a minor effect on the overall value of the product as the items left out are of relatively low-value.

Pete recommends having a ‘Scope Buffer’– PBIs that can be dropped, or postponed to a later release. This way, even if they remain incomplete by the release, a high-value product can still be delivered without delay. The ‘MoSCoW prioritization’ can help product owners prioritize their PBIs. It groups PBIs into the following categories – ‘Must have’, ‘Should have’, ‘Could have’ and
‘Won’t have’. ‘Must have’ items should be placed high on the PBI list, followed by ‘Should have’, ‘Could have’ and finally, ‘Won’t have’ items. This method ensures that no matter what, a minimum viable product can be created within the planned time frame.

Product ownership

One certain thing product owners should know – things won’t go as planned!

How to work effectively with remote teams

Establishing connections with people you haven’t met, and who are in a geographically separate location is not easy. However, with remote work becoming the norm, there are ways to navigate some of the oft-encountered barriers. Pete believes that trust is what enables a product owner to get the most out of a team.

Trust is a force multiplier

In a low-trust environment, developers don’t ask questions, hide mistakes, ignore concerns, cover up problems, and pretend all is well until it is too late. However, in a high trust environment, developers are open and honest – questions are asked, thus fewer mistakes are made, and concerns are brought up, ensuring that problems get tackled early. An environment that promotes trust and values honesty (even if that honesty brings up things the product owner doesn’t like to hear!) is vital when working with remote teams.

Pete Deemer

Some ways through which trust can be built:

  • Establishing real, human relationships at the start: Pete encourages any product owner to take the extra time, spend the money and meet the development team at least once at the beginning of a partnership. This helps the team see the product owner as a real person, and helps establish a connection between all parties. If it is not possible to physically visit the team, engaging in online icebreakers and team activities that encourage people to share information about themselves, their interests outside of work etc. help to promote the relationship between product owners and their teams.

Pete Deemer Calcey

  • Understand the inherent power differential: There will always be a difference in power dynamics between the team and the product owner, and it is important for product owners to be aware of this when working with the team.
  • Show respect, and reward honesty
  • Be mindful of language and communication barriers and biases: Establishing a common language for communication is important but it is equally important to be aware of the biases that come through the use of a language – such as English- that may not be the first language of either the team or the product owner. A key fact to remember is that proficiency of language is not an indicator of professional capability.
  • Agree on the ‘rules of the game’ and play by the rules: Eg. A product owner should not ask developers to be honest and then penalize them for their honesty.
  • Scale the communication and be aware of signal-to-noise: One of the main problems encountered by product owners and teams is miscommunication. Depending on how sensitive the matter is, the most suitable method of communication should be utilized. Eg: A text is alright for a quick clarification but an email would provide greater detail, while an audio call or video call would be the most preferable for a longer back and forth discussion. In-person meetings are the best choice for any high priority communication, as richness and fidelity is highest in this instance. It is up to the team and the product owner to determine the best method of communication for the circumstance.

Successful products don’t teach you anything, it’s the failures that do!

Building an exceptional and valuable product is no easy feat—both product owners and development teams have a responsibility to each other. Pete hopes that by sharing lessons learnt and knowledge acquired, he may help others avoid pitfalls while incorporating practices that benefit the team and product owners alike.

Also, if you want to learn more about how to build great software and digital products using a remote team, go check out our Remote Team Playbook. A complete recording of Pete’s webinar is available here.