When an SBOM becomes operationally useful: lessons from Eclipse Kura

When an SBOM becomes operationally useful: lessons from Eclipse Kura

Supply chain security has become a critical topic in the security world in recent years, and while SBOMs are a foundational piece, they are still infrequently generated and even less frequently used in a way that meaningfully improves software supply chain security.

To address this gap, the OCX sessionGenerating SBOM: Understanding the Why, Mastering the How and Learning from Eclipse Kura’s Experience” focuses on simplifying SBOM adoption through a combination of practical resources and real-world project experience. Drawing on the Eclipse Foundation’s security tooling and guidance, Ioana Iliescu, Security Team Tech Lead at the Eclipse Foundation, and Mattia Dal Ben, Principal Software Engineer and Eclipse Kura committer, walk through how SBOM generation, supporting infrastructure, and automation come together in practice, based on Eclipse Kura’s adoption journey.

SBOMs are often introduced through compliance or customer-driven requirements. When their value is perceived as external rather than operational, teams struggle to prioritise the initial effort required for meaningful adoption. As Ioana Iliescu explains:

When the benefit feels like it’s external, and delayed, but the cost is internal, and immediate, SBOMs tend to lose out to feature work.

The consequences of this deprioritisation become visible when a vulnerability is disclosed. At that point, teams must rely on the information they already have about their software composition. As Iliescu notes, traditional dependency management reflects intent rather than reality:

It’s what the project declares itself that it depends on, and SBOM shows what’s actually shipped, including not only direct, but also transitive dependencies, and the relationship between them.

This visibility into transitive dependencies directly affects how quickly teams can assess exposure and respond to software supply chain incidents.

Eclipse Kura’s experience also shows that SBOM generation on its own is not sufficient to create security value. As Mattia Dal Ben points out:

SBOM generation is a small piece of the overall picture. You have to generate them automatically, keep them updated, store them, ingest them, and perform continuous analysis on them.

Integrating SBOM generation into existing build systems and CI/CD pipelines required addressing compatibility issues and project-specific edge cases, even with a mature SBOM tooling ecosystem.

Attendees of this session will gain a concrete understanding of what it takes to generate and move SBOMs from static artefacts into operational security inputs. The talk will walk through tooling choices, integration decisions, and edge cases encountered in Eclipse Kura, alongside the Eclipse Foundation infrastructure that supports SBOM generation, storage, and analysis at scale.

Learn more at OCX in Brussels

Register and attend this session in Brussels to see how SBOMs are operationalised in practice, based on real Eclipse Foundation infrastructure and the Eclipse Kura team’s experience.

Image
OCX

Daniela Nastase


The Day Facebook Went Offline: A Case Study in Centralization

In October 2021, Facebook disappeared from the internet for roughly six hours.

Its core platforms — Instagram and WhatsApp — went down with it. For many users it felt like an unusually long outage. For businesses, it meant lost revenue. For engineers, it exposed something more structural: how centralized modern internet infrastructure has become.

This wasn’t a breach. It wasn’t ransomware. It wasn’t a nation-state attack.

It was a routing failure.

What Actually Happened

The root cause was a configuration change affecting BGP (Border Gateway Protocol). BGP is how networks announce their IP prefixes to the rest of the internet. When Facebook’s routes were withdrawn, its IP space effectively disappeared from global routing tables.

No routes → no traffic.

DNS servers became unreachable. Domain names stopped resolving. Internal tools that relied on the same infrastructure went down. Even physical access systems reportedly failed because they depended on the internal network.

This is a critical point: the systems required to fix the outage were partially affected by the outage itself.

That’s not a dramatic failure. It’s a coupling problem.

When a Company Becomes Infrastructure

Facebook is not just an app. It functions as:

an identity provider

an advertising platform

a storefront for small businesses

a messaging backbone in many countries

When such a platform fails, the impact extends beyond its own users. It affects commerce, media distribution, authentication workflows, and customer support pipelines.

The outage highlighted a broader issue: private platforms increasingly act as public infrastructure.

Centralization increases efficiency.
It also increases blast radius.

Tight Coupling at Scale

Large platforms optimize for integration. Shared identity systems, shared networking layers, shared operational tooling — all of it improves speed and coordination.

But integration also creates shared failure domains.

When external routing fails and internal tooling depends on the same routing layer, recovery becomes slower and more complex. Redundancy inside one organization is not the same as independence across systems.

This is the architectural trade-off centralization often hides.

Why Scale Doesn’t Eliminate Fragility

Large tech companies invest heavily in reliability engineering. They measure uptime in decimals. They build multiple data centers across continents.

Yet high availability percentages don’t eliminate systemic risk. They reduce average downtime — but they don’t necessarily reduce the impact of rare failures.

When billions of users rely on a single entity, even statistically rare events become globally disruptive.

Resilience isn’t just about uptime.
It’s about limiting the scope of failure.

The Centralization Trade-Off

It’s easy to frame centralization as purely negative, but that would be simplistic.

Centralized systems offer:

simpler identity management

unified moderation

cost-efficient global scaling

consistent user experience

The problem isn’t centralization itself. It’s unexamined dependency.

Users and businesses optimize for convenience. They rarely evaluate systemic risk when choosing platforms. The risks become visible only when something breaks.

The 2021 outage briefly made that trade-off visible.

Is Decentralization the Answer?

After major outages, discussions about decentralization resurface. Federated networks, distributed architectures, blockchain systems — alternatives appear attractive.

But decentralization alone doesn’t guarantee resilience. Without operational discipline and independent governance, control can simply recentralize around infrastructure providers or protocol maintainers.

Distribution reduces certain risks.
It introduces others.

Architecture still matters.

The Structural Lesson

Complex systems fail. That’s inevitable.

The key question is not whether failure happens — it’s how far failure propagates.

When authentication, communication, and commerce converge inside a handful of companies, outages become systemic shocks. The internet may look decentralized on the surface, but power and dependency are increasingly consolidated.

The Facebook outage wasn’t just downtime. It was a reminder that integration and efficiency often come at the cost of optionality.

And optionality is a core component of resilience.

I write about infrastructure risk, privacy, system design trade-offs, and long-term software resilience at:

https://50000c16.com/

If you’re building systems that millions depend on, centralization isn’t just a business decision — it’s an architectural responsibility.

Using Docker Compose Profiles to unit tests part of the application

Compose profiles let you group services so you can start only a subset of your stack for specific scenarios (e.g., lightweight testing vs. full production).

In your docker-compose.yml, add a profiles list to each service you want to group:

services:
  postgres:
    profiles: ["core"]
    ...

  jobrunner:
    profiles: ["core"]

  nextjs:
    profiles: ["full"]

  authserver:
    profiles: ["full"]

How it works

  • docker compose up (no --profile)
    Starts only services without a profiles key.
    In the example above, nothing would start unless you define a default (non-profiled) service.

  • docker compose --profile core up
    Starts only services tagged with core (postgres, jobrunner).
    Ideal for a lightweight testing stack.

  • docker compose --profile full up
    Starts only services tagged with full (nextjs, authserver).

  • Multiple profiles are supported:

  docker compose --profile core --profile full up

This allows you to maintain one Compose file while running minimal stacks for testing or the full environment as needed.

If a service should always run, omit the profiles key. It will start regardless of profile flags.

Multiple Databases and Profiles

Docker Compose does not allow multiple services with the same name—even across different profiles.

If you need both a production database and a unit test database, give them unique service names and assign each to its own profile:

services:
  app-db:
    profiles: ["prod"]

  unit-test-db:
    profiles: ["test"]

Database Configuration Strategy

  • Your application containers use one DATABASE_URL at a time.
  • Each profile startup determines which database the app connects to.
  • For example:

    • In a test profile, set DATABASE_URL to unit-test-db.
    • In a prod profile, set DATABASE_URL to app-db.

There is no runtime toggling — each profile spin-up decides which database your app connects to based on the environment variables provided at startup.

Alternative Approach

Instead of profiles, you can maintain separate Compose files:

  • docker-compose.test.yml
  • docker-compose.prod.yml

Each file can hardcode the correct database host.

This provides clearer separation but requires maintaining multiple files.

Concrete Example with unit-test-db and app-db

Below is a clean, complete working example.

1️⃣ docker-compose.yml

services:
  # -----------------
  # Databases
  # -----------------

  unit-test-db:
    image: postgres:15
    container_name: unit-test-db
    profiles: ["test"]
    environment:
      POSTGRES_USER: test
      POSTGRES_PASSWORD: test
      POSTGRES_DB: testdb
    ports:
      - "5433:5432"

  app-db:
    image: postgres:15
    container_name: app-db
    profiles: ["prod"]
    environment:
      POSTGRES_USER: prod
      POSTGRES_PASSWORD: prod
      POSTGRES_DB: proddb
    ports:
      - "5432:5432"

  # -----------------
  # Application
  # -----------------

  app:
    build: .
    depends_on:
      - unit-test-db
      - app-db
    environment:
      DATABASE_URL: ${DATABASE_URL}

2️⃣ What DATABASE_URL Should Look Like

Inside Docker, the hostname is the service name.

Test profile → connect to unit-test-db

postgresql://test:test@unit-test-db:5432/testdb

Prod profile → connect to app-db

postgresql://prod:prod@app-db:5432/proddb

3️⃣ Running with Different Environment Variable Sets

✅ Option A — Use Separate .env Files (Recommended)

.env.test

DATABASE_URL=postgresql://test:test@unit-test-db:5432/testdb

.env.prod

DATABASE_URL=postgresql://prod:prod@app-db:5432/proddb

Start test stack

docker compose --profile test --env-file .env.test up

Start prod stack

docker compose --profile prod --env-file .env.prod up

This is the cleanest and most maintainable approach.

✅ Option B — Inline Environment Variables

Test

DATABASE_URL=postgresql://test:test@unit-test-db:5432/testdb 
docker compose --profile test up

Prod

DATABASE_URL=postgresql://prod:prod@app-db:5432/proddb 
docker compose --profile prod up

4️⃣ What Actually Starts

When you run:

docker compose --profile test up

Only:

  • unit-test-db
  • app

will start.

When you run:

docker compose --profile prod up

Only:

  • app-db
  • app

will start.

Clean Mental Model

Profile DB Container DATABASE_URL host
test unit-test-db unit-test-db
prod app-db app-db

The service name becomes the DNS hostname inside Docker’s network.

Each profile launch defines:

  • Which database container runs
  • Which DATABASE_URL your app uses

There is no live switching — environment selection happens at startup.

Instance Variables and Instance Methods in Python

In Object-Oriented Programming (OOP), two of the most important concepts are:

  • Instance Variables
  • Instance Methods

If you don’t understand these properly, OOP will always feel confusing.

So let’s break everything down step by step in the simplest way possible.

Let’s Start with a Simple Real-Life Example

Imagine a classroom. Learn How Instance Variables and Methods Actually Work in Python

You (teacher) are the class blueprint.
Your students are objects.

Every student:

  • Has a name
  • Has a roll number
  • Has marks
  • Can perform actions (submit homework, give test, etc.)

Now here’s the important part:

Even though all students come from the same class,
each student has their own data.

That “own data” is exactly what we call:

Instance Variables

And the actions they perform?

Instance Methods

What is an Instance Method in Python?

An instance method is a method that belongs to an object and works on that specific object.

Definition (Simple Words)

An instance method:

  • Is defined inside a class
  • Has self as its first parameter
  • Can access and modify instance variables

Why Do We Need self?

When we create multiple objects from a class, each object has its own data.

self tells Python:

Work with the current object that is calling this method

Example

class User:
    def activate(self):
        self.is_active = True

If we create two objects:

user1 = User()
user2 = User()


When we call

user1.activate()

Python automatically does this internally:

User.activate(user1)

That means:

self = user1
So instance methods always operate on the object that calls them

What is an Instance Variable in Python?

An instance variable is a variable that belongs to a specific object.

It is created using:

self.variable_name = value

Important Concept

Instance variables:

  • Store data
  • Are unique for each object
  • Exist inside the object
  • Can be different for different objects
    Example
def set_email(self, email):
    self.email = email

When we call:

user1.set_email("shameel@hasabtech.com")

It becomes:

user1.email = "shameel@hasabtech.com"

Now email belongs only to user1.

If we check user2, it still does NOT have email until we set it.

That is why instance variables are object-specific.

Complete Example with Deep Explanation

Let’s build a proper example and understand what happens step by step.

class User:

    def activate(self):
        self.is_active = True

    def deactivate(self):
        self.is_active = False

    def set_email(self, email):
        self.email = email

    def show_status(self):
        print(f"{self.email} is {'active' if self.is_active else 'not active'}")

Step 1: Creating Objects (Instances)

user1 = User()
user2 = User()

When we execute this:

  • Python creates two separate objects in memory
  • Each object has its own space to store data
  • Both objects have access to all instance methods

Important: Object and Instance mean the same thing.

What Happens Inside Memory?

Even though both objects come from the same class blueprint:

  • user1 has its own data storage
  • user2 has its own data storage

They do NOT share instance variables.

This is the beauty of OOP.

Understanding Instance Methods in Action

Now let’s call:

user1.set_email("shameel@hasabtech.com")

What happens?

  1. Python sees user1
  2. It passes user1 automatically as self
  3. Inside method:
self.email = email

Becomes

user1.email = "shameel@hasabtech.com"


Now:

  • user1 has email
  • user2 still has nothing

This proves instance variables belong to objects.

Adding More Data to Objects

Now:

user1.activate()
user2.deactivate()

This creates:

user1.is_active = True
user2.is_active = False


Again:

  • Each object has its own is_active
  • They are completely independent

This independence is the core idea of instance variables.

Why Methods are Called Instance Methods?

Because they:

  • Work with instance variables
  • Depend on the object
  • Use self to access object data

For example:

def show_status(self):
    print(f"{self.email} is {'active' if self.is_active else 'not active'}")

This method:

  • Reads self.email
  • Reads self.is_active
  • Prints data of that specific object

So this method behaves differently for each object.

That’s why it is called an instance method.

Common Beginner Confusion

Many students think:
Is self a keyword?
No.

self is just a naming convention.

You can write:

def activate(myobject):

def activate(myobject):

But by convention, we always write self

Why This Concept is Very Important?

If you understand instance variables and instance methods, you can:

  • Build login systems
  • Create user management systems
  • Understand Django models
  • Work with APIs
  • Design real-world applications

Without understanding self, OOP will feel complicated.

Final Understanding

Whenever:

  • You attach data using self → It becomes an instance variable.
  • You define a method with self → It becomes an instance method.

Each object:

  • Has its own data
  • Shares method structure
  • Behaves independently

Summary

  • Object = Instance. They mean the same thing.
  • Instance Variables are the “adjectives” (data) that describe the object.
  • Instance Methods are the “verbs” (actions) the object can perform.
  • self is the bridge that connects the method to the specific object’s data.
  • Each object has its own copy of instance variables

Stay connected with hasabTech for more information:
Website | Facebook | LinkedIn | YouTube | X (Twitter) | TikTok

Cadre d’Invention Validée: Intelligence Artificielle (v2) – Lazarus Prometheus

Cadre d’Invention Validée: Intelligence Artificielle (v2)

Type: INVENTION
Domain: Research Engineering
Date: 2/20/2026

🎯 Potentiel

Réduction du taux d’échec des inventions publiées via gating quantifié (objectif: -50% erreurs / -70% répétition).

📋 Description

Invention guidée par signaux web: le focus est “Intelligence Artificielle”. Plutôt que du buzzword bingo, l’objet est un cadre reproductible qui force une preuve (PoC + baseline + métriques) avant de publier. Les concepts (Energy conservation, Genetic algorithms, Zero-sum games) servent à définir des fonctions d’impact/risque et une dynamique d’amélioration.

🧭 Analyse de la concurrence (pré-publication)

Sujets fréquents: AI Models, theoretical physics, ai safety, social science, engineering, product updates

🔬 Concepts Sources

  • Energy conservation
  • Genetic algorithms
  • Zero-sum games

🚀 Implémentation Proposée

  1. Définir un baseline mesurable lié à “Intelligence Artificielle”
  2. Écrire un prototype minimal (fonctionnelle, instrumentation incluse)
  3. Évaluer sur un jeu de tests/simulateur (3 scénarios minimum)
  4. Comparer baseline vs prototype et calculer delta
  5. Décider publication seulement si delta est prouvé

Généré par Lazarus Prometheus – Curiosity Drive
Legacy Score: 10/12

You Don’t Need Motivation — You Need a System

One of the biggest lies beginners believe is this:

“When I feel more motivated, I’ll study seriously.”

Motivation feels powerful.

But it’s unreliable.

And if your progress depends on it, you will stop.

Not because you’re incapable.
But because motivation fades.

Every time.

Motivation Is Emotional. Growth Is Structural.

Motivation is emotional energy.

It comes when:

  • You watch an inspiring video
  • You read a great article
  • You imagine your future career
  • You feel behind in life

But emotional energy fluctuates.

Structure doesn’t.

The developers who improve consistently are not more motivated.

They have better systems.

The Real Problem Beginners Face

Most beginners rely on:

  • Random study sessions
  • Late-night bursts of energy
  • Weekend “I’ll change my life” moments
  • Long study marathons

This creates a cycle:

Intense effort → Exhaustion → Break → Guilt → Restart → Repeat.

That’s not growth.

That’s emotional dependency.

What a System Looks Like

A system is boring.

And that’s why it works.

Example:

  • Study 45 minutes per day
  • Build something small every week
  • Review what you learned every Sunday
  • Track progress visibly

No drama.
No emotional spikes.
Just repetition.

Why Small Daily Effort Beats Intense Study

Let’s compare:

Person A:

Studies 6 hours on Saturday.
Does nothing during the week.

Person B:

Studies 1 hour per day.

After one month:

Person A: ~24 hours

Person B: ~30 hours

But the real difference isn’t time.

It’s consistency.

Consistency builds:

  • Pattern recognition
  • Memory reinforcement
  • Deeper understanding
  • Real confidence

Systems Reduce Friction

One hidden benefit of systems:

They remove decision-making.

If you wake up and ask:
“Should I study today?”

You already lost.

A system answers that for you.

You don’t negotiate with yourself.

You execute.

The Developer Advantage

In software engineering, everything is built around systems:

  • CI/CD pipelines
  • Code review processes
  • Deployment flows
  • Testing automation

Why?

Because systems reduce human error.

Your learning should follow the same logic.

A Simple System for Beginner Developers

Here’s a practical one:

Daily

  • 30–60 minutes coding
  • No zero days

Weekly

  • Ship one small improvement or mini project

Monthly

  • Build one complete small project

That’s it.

If you follow this for 6 months, your level changes dramatically.

Not because of motivation.

Because of accumulation.

Where AI Fits In

AI can help reduce friction inside your system.

Use it to:

  • Clarify concepts quickly
  • Debug faster
  • Generate starter structures
  • Review your code

But remember:

AI supports the system.

It does not replace discipline.

Final Thought

Motivation starts journeys.

Systems finish them.

If you want to become a developer, stop waiting to feel ready.

Create a structure.

Follow it even when you don’t feel like it.

Especially when you don’t feel like it.

That’s how real progress happens.

Java to Kotlin Conversion Comes to Visual Studio Code

At JetBrains, we aim to make Kotlin development as accessible and efficient as possible across the entire ecosystem. While IntelliJ IDEA remains the premier IDE for Kotlin, we recognize that many developers use Visual Studio Code for a variety of tasks and projects.

To help streamline the transition from Java to Kotlin for VS Code users, we are pleased to introduce the Java to Kotlin (J2K) converter extension.

Download the Extension

Seamless Conversion in VS Code

This new extension allows you to convert individual Java files into Kotlin code with a simple context menu action, significantly reducing the manual effort required when migrating legacy codebases or switching languages mid-project.

Because the extension leverages the same underlying engine used in our primary IDEs, you can expect a reliable conversion that respects Kotlin idioms and syntax requirements.

See it in action

The converter is designed to be unobtrusive and easy to use. Watch the short demo below to see how it handles the conversion process.

How to get started

To begin using the converter, simply:

  1. Install the Java to Kotlin Converter extension from the Visual Studio Marketplace.
  2. Open any .java file in your workspace.
  3. Right-click anywhere in the editor or on the file in the Explorer and select Convert to Kotlin.

Our Commitment to the Ecosystem

This extension is part of our ongoing effort to support Kotlin users wherever they choose to write code. It joins other initiatives aimed at improving the developer experience outside of IntelliJ IDEA, such as the Kotlin LSP, which provides IDE-independent language support via the Language Server Protocol.

As this is a new release, we highly value your feedback. If you encounter any issues or have suggestions for improvements, please report them on YouTrack or through the extension’s marketplace page.

TeamCity 2025.11.3 Is Here

Today we’re rolling out another bug-fix update for your TeamCity 2025.11 On-Premises servers. This update addresses a number of issues, inlcuding:

  • QoL updates for the bundled IntelliJ Platform Plugin;
  • Variable GID value for Docker inside TeamCity Docker images;
  • NPE login fails;
  • Connection settings are reset after changing the parent project.

All TeamCity bug-fix updates include performance and security improvements, so we recommend that you never skip these minor updates. See TeamCity 2025.11.3 Release Notes for the complete list of resolved issues.

Why update?

Staying up to date with minor releases ensures your TeamCity instance benefits from the following:

  • Performance improvements.
  • Better compatibility with integrations.
  • Faster, more stable builds.
  • Enhanced security for your workflows.

Compatibility

TeamCity 2025.11.3 shares the same data format as all 2025.11.x releases. You can upgrade or downgrade within this series without the need for backup and restoration.

How to upgrade

  1. Use the automatic update feature in your current TeamCity version.
  2. Download the latest version directly from the JetBrains website.
  3. Pull the updated TeamCity Docker image.

Need help?

Thank you for reporting issues and providing feedback! If you have questions or run into any problems, please let us know via the TeamCity Forum or Issue Tracker.

Happy building!

The Missing Link Between AI and Business Value

Let’s call a spade a spade. Some enterprises are already using AI agents, but very few can explain their impact on business performance.

Metrics such as DORA, SPACE, and developer experience indicators captured through third-party platforms offer insight into delivery velocity and developer quality of life, but it is still difficult to cleanly map this all the way to business impact. 

Unless you work directly on model development, model metrics themselves rarely determine whether AI is creating enterprise value.

The gap between technical performance signals and sustained business outcomes is an obstacle to scaling AI responsibly.

From technical metrics to business value

Abstract benchmarks such as SWE-Bench Pro and Tau2-bench are directionally useful in selecting AI tools, but can be orthogonal to how these tools perform in enterprise systems. An agent that performs well in a controlled environment can fail once integrated into production workflows. What matters is not benchmark scores, but the impactfulness, traceability, and resilience of AI systems under real-world conditions. 

Recent data underscores the urgent need to find an accurate way of measuring these variables. Though 88% of employees use AI at work today, just 5% use it “in a transformative way”, according to the EY 2025 Work Reimagined Survey.

Blindly adopting AI is unlikely to be fruitful. Enterprises should instead experiment with and evaluate AI through operational metrics on the systems they are accountable to build and operate. The focus should be on the lifetime cost of maintaining systems, the average time humans spend over baseline, and throughput as a function of Total Cost of Ownership (TCO).

Auditability matters for tracing decisions and meeting governance needs, while human readability ensures teams can understand and manage system behavior now, and later. These are table stakes for technical teams to have in place as they adopt AI at scale.

The ROI problem

Every enterprise wants to link AI to ROI, but the data rarely aligns. The problem is not limited to model telemetry. AI is embedded into enterprise systems and assigned responsibility for specific parts of the SDLC and operational workflows.

Evidence of its impact must therefore span system behaviour, human intervention, and downstream business KPIs. These signals live in different systems and move on different timescales, which creates a gap between AI activity and measurable business outcomes. This is why most organizations rely on proxies or assumptions rather than proof.

Closing the gap

The next generation of AI orchestration platforms will need to close this gap by correlating technical performance with operational and financial signals. When those systems mature, ROI will shift from being an abstract target to a measurable outcome grounded in data.

The impact of this gap is already visible in enterprise outcomes. The WRITER 2025 Enterprise AI Adoption Report found that organizations without a formal AI strategy report only 37% success when adopting AI, compared with 80% for those that tie performance to clear operational outcomes.

The data is unambiguous. Only when an organization measures technical and operational signals together does it finally gain a true picture of AI’s value.

Towards continuous benchmarking

What underlies enterprise AI is not static. Data drifts, workflows evolve, and compliance obligations expand. Measurement must therefore become a continuous feedback loop rather than an annual report. 

The same principle should apply across the enterprise: Performance metrics should remain stable, but they must either stay independent of changing conditions or explicitly measure those changes over time.

Measuring what matters

Meaningful AI performance measurement is not about bigger numbers or more dashboards. It is about connecting operational signals with business truth. 

Enterprise leaders must grapple with model performance alongside how intelligently it scales, how transparently it operates, and how clearly its impact can be proven.

Taking benchmark numbers at face value resembles trusting a car manufacturer’s fuel efficiency without ever driving the car to see if it holds up in real conditions. 

Only when these can be addressed with real data will AI become a truly accountable part of the enterprise stack.

The real question for leaders is simple: Are you measuring the numbers that prove AI is working in practice, or just parroting back the numbers on public benchmarks?

How Students Are Using AI-Powered Hints in Programming Courses

Learning to code in an online course can feel like hitting a wall again and again, without much help moving forward. Our research team wanted to change that. That’s why our Education Research team built an AI-powered hints tool that doesn’t just point out errors but helps students understand them. As described in our blog post last summer, students can use the tool in JetBrains Academy courses to get tailored guidance nudging them towards the solution instead of staring blankly at a stubborn piece of code or waiting for help on a forum. 

Try our plugin

Since then, our researchers have investigated how students actually use the tool and published the results in a paper that they will present at the Technical Symposium on Computer Science Education (SIGCSE TS) February 19 in St. Louis, Missouri. In this blog post, we will explain how the study was set up, its results, and our analysis.

Learning to code with the help of smart feedback

In the last post, we covered the history of online learning and the importance of feedback in any type of learning environment. Online programming courses can differ a lot in format, especially in how they encourage students to practice writing and running code within the course. 

Many online learning platforms, such as Udemy, Codecademy, and edX, provide online editors for students to complete the exercises. Learning programming inside a code editor is easier than installing and learning new software, but in the long run it means that the student is unfamiliar with the relevant professional environment. Other platforms, such as Hyperskill, offer a hybrid option, where the student can also learn in a professional integrated developer environment (IDE) if they choose to do so. The downside of a hybrid course is that some students might not choose to use the IDE, for example if they consider it a hurdle to install and learn to navigate new software.

For our JetBrains Academy courses, we released the JetBrains Academy plugin a few years ago, which lets educators build complete courses directly inside the IDE. With this setup, students can read theory and immediately apply it by solving practical tasks in the very same environment they use to write code. As a result, they can become comfortable working in IDEs early on and are better prepared for real-world software development roles. 

A recent addition to our plugin is the AI-powered hints tool, developed by our Education Research team. The tool leverages powerful IDE features by combining static analysis and code quality checks with LLMs to provide personalized hints to students. In the previous paper, we conducted evaluation studies to gauge how useful students found the tool while learning, as described in the previous blog post.

To help students even further, we wanted to know more about how they are really interacting with the tool and discover possible new behavior patterns. In the rest of this blog post, we will talk about exactly this: how students are actually interacting with the AI-powered hints tool.

Smart feedback in online learning

Recent years have seen many studies exploring how students interact with intelligent tutoring systems, offering useful directions for future system design. In this short subsection, we will provide a summary of recent studies, plus describe what our study adds to the field.

Researchers in this study examined an intelligent next-step hint system to understand why students ask for help, looking at factors such as time elapsed and code completeness after each hint, and identifying reasons like difficulty starting an exercise. This work provides general insights into students’ hint-seeking patterns, but without the details (e.g. keystroke data). This study analyzed when and how to provide feedback and hints using keystroke datasets annotated with points where experts would intervene, but did not look at students’ actual interactions with hint systems. 

In this paper, researchers investigated how different hint levels support task completion and why students request hints, using a think‑aloud study with pre‑ and post‑tests for 12 students. However, we would have liked to see broader usage patterns or how learners cope with unhelpful hints.

More recently, researchers studied how students use on-demand automated hints in an SQL course and how these hints affect learning, using A/B testing to identify behavioral differences and simple patterns. For example, they found that students often request a second hint within 10 seconds of the first one. While this research sheds light on basic behavioral trends, it mainly asks whether adding a hint system supports learning, rather than closely examining the interaction patterns themselves. 

Overall, prior work has focused on why and when hints are given, but far less on how students actually work with them in practice. In our study, we applied process mining techniques to uncover detailed behavioral patterns in how students interact with hints while they solve programming tasks. In addition, we conducted interviews with a subset of the students to learn more about how they interact with the hints tool in specific scenarios.

Our investigation into students’ behavioral patterns

With our study, we wanted to better understand how the students are really interacting with the tool, as well as how students navigate situations with less helpful hints. Specifically, our two research questions were:

  1. What behavioral patterns can be identified based on students’ interaction with the next-step hint system?
  2. What strategies do students use to overcome unhelpful hints?

Our study collected data in two steps. First, we collected information about all of the students’ interactions with the hints system, analyzing these interactions quantitatively. Then, we selected a subset of six students to interview for about 30 minutes, so that we had a qualitative and in-depth complement to the quantitative analysis. In the next subsection, we will tell you about the methodology for each step. The subsection after that will present the results and analysis.

Quantitative behavioral analysis

For the first part of the study, we analyzed detailed problem-solving data from first- and second-year Bachelor’s students working on programming tasks in our introductory Kotlin course. The selected projects represent different levels of complexity and cover basic topics such as variables, loops, conditional operators, and functions. The 34 participants had previously completed at least one programming course in another language, and they had no experience with Kotlin before. We asked these students to complete three projects from the course, using the hint system whenever they wanted. 

We used KOALA to capture keystrokes, IDE actions, the windows they accessed, and rich information about their hint usage. Hint-usage information included details like when hints were requested and for which specific tasks. The resulting dataset contains:

  • 6,658,936 lines of code
  • 1,364,943 IDE activity events
  • 960 textual hint requests
  • 453 code hint requests 

Note: This dataset is open source, and we encourage you to take a look. It can be used for further analysis in a research project, or even just for educators to see how beginner learners are programming.

Process mining and our dataset: Preliminaries

After collecting this data, we conducted a quantitative analysis of the students’ behavior. To do this, we used a process mining technique. Process mining turns log or event data into behavioral models using action analysis, taking into account both event frequency and sequence. In business settings, the technique can help companies see where improvements are needed, as is argued for in this process mining manifesto, and save a lot of money, for example, by finding instances of duplicate payments or minimizing complex order delays.

Although it is primarily used in industry settings, process mining has already been applied in educational domains (see this paper and this paper for examples). As far as we know, we are the first to use process mining to analyze programming hint systems.

To apply this technique to our hint-systems dataset, we first extracted all hint-request sessions from the dataset (1,050 in total). The data from these sessions are the input for the analysis, which uses specialized algorithms to map the activities and transitions between them.

The sessions began when a student requested a hint. By clicking Get Hint or Retry, they ended when it was clear that the hint was processed, either by accepting the code hint or closing the text hint banner. The session was also considered “over” if the student ran into an error or there was no activity for five minutes.

We also defined different activities within sessions. Here are the activities:

  1. Hint Button Clicked: The student clicks Get Hint to generate a hint.
  2. Hint Retry Clicked: The student clicks Retry to regenerate a hint.
  3. Hint Code Generated: The code hint is generated. By tool design, this action is triggered every time Get Hint is clicked.
  4. Hint Banner Shown: The banner with a textual hint is shown to the student. Although this action is carried out by the system, not by the student, it marks the moment when the student has viewed the hint. This action will be used for a more detailed analysis.
  5. Show Code Hint Clicked: The student clicks Show Code to show a code hint.
  6. Hint Accepted: The student clicks Accept to accept a code hint.
  7. Hint Banner Closed: The student closes the textual hint banner.
  8. Hint Canceled: The student clicks Cancel to decline a code hint.
  9. Error Occurred: This action includes cases where internal errors occurred during the hint generation process, e.g. problems with the internet connection, or when the LLM did not provide a response.

All sessions began with items 1 or 2, i.e. clicking Get Hint or Retry. A session could end with different activities, with the most common being items 6, 8, and 9: Hint Accepted, Hint Canceled, or Error Occurred

In the analysis, we coded the sequence of activities within each session. We also grouped the sessions into the following three main types:

  • Positive Student accepted the code hint
  • Neutral Student saw a hint but didn’t clearly accept or reject it
  • Negative Student canceled a hint or got an error

The first and last types are straightforward: For the positive type, the session ended when the student accepted the code hint. For the negative, the session ended because the student did not use the hint, having received an error or actively cancelled the hint. 

The neutral type will be looked at in more detail in the next section, as it required additional analysis to determine how helpful the student found the hint. We know by the actions carried out that the student saw a text or code hint, but they proceeded without explicitly accepting the code hint. Examples of neutral sessions include the following (Hint Code Generated has been omitted, as it automatically occurs as the second step in each):

  • Hint Retry Clicked → Hint Banner Shown 
  • Hint Button Clicked → Hint Banner Shown → Show Code Hint Clicked
  • Hint Retry Clicked → Hint Banner Shown → Hint Banner Closed

The numbers below shows the distribution of session types among the 1,050 total sessions.

  • Positive: 299 (28%)
  • Neutral: 420 (40%)
  • Negative: 284 (27%)

The above shows that strictly positive sessions make up about a third of all sessions. As will be discussed below, we found out that the neutral cases often actually were successful in helping the students with their tasks, even if not positive as defined above. Namely, in these cases students clearly analyzed and used the content of the hints but did not automatically apply them.

Note that 266 of the 284 (94%) negative sessions ended with errors, rather than the student cancelling the hint. These errors were either internet connection issues on the student side or a system issue on our side; we have already fixed any such system errors. In addition to these three types listed above, we collected 47 data points, accounting for 4,5% of the sessions, that were not classifiable based on the available data. 

With these preliminary data about the “events”, we were able to move on to applying the process mining technique for our analysis. The next subsection describes these results. 

Process mining and our dataset: Analysis

We turned the data described in the previous subsection into a map of how students moved through the system. This sort of map is also called a process behavior graph. The graph for our dataset is displayed below; as it is quite complex, we will explain the most important parts individually.

ai-powered hints tool process behavior graph

In the above graph, some boxes are colored, while others are not. The blue boxes at the top represent activities that begin a session, either by generating a new hint or retrying a hint; the red boxes on the left represent activities that end a negative session; the green box at the bottom is the activity that can end a positive session; and the other activities represent activities that can otherwise happen during a session. 

The arrows indicate the direction of transitions, and the numbers next to the arrows indicate the average time in seconds for each transition. Longer times, particularly those around 900 seconds, indicate that the student was solving the task and then returned for another hint. In those cases, a new event would have been triggered.

Each arrow’s transparency and thickness represent information about the frequency and average prevalence. As can be seen in the fragment below, the most frequent transitions occurred from the blue box Hint Button Clicked to white boxes Hint Code Generated and then to Hint Banner Shown. 

The arrows that circle back to themselves, such as in the snippet below, represent those instances in which students repeatedly clicked the same button. In the case of the white box Show Code Hint Clicked, we think this suggests that the student might not have understood how the code hint worked, and that a future iteration of the tool would need to provide more explanation to help the student.

Looking more closely at the negative sessions, we can see in the following image two arrows coming from the red box labelled ERR. The right arrow with an 18.7-second average leads to HRC, or Hint Retry Clicked. The left arrow with > 900 s leads to HBC, or Hint Button Clicked.

These patterns indicate that even when the student encountered an error, such as a bad internet connection, they persisted in generating a new hint, either within the same task or in another. This is an important insight because it shows that students are motivated to use the hints tool even when encountering errors or negative scenarios.

In the image below, we can also see that between the blue Hint Button Clicked and various white boxes, there is an arrow going back to the blue HBC button. We interpret these transitions as indicating that the students modified their code to generate additional variations of the same hint before integrating the information into a solution. That is, the pattern suggests that they did not simply click Accept Hint, but tried to solve the task themselves.

In the classification part of the analysis described in the previous subsection, these types of activities usually comprised the sessions labelled as neutral, as they often did not end with the student accepting the hint. These and other unexpected patterns were why we wanted to hear more from these students about why they behaved the way they did.

Qualitative behavioral analysis

As described in the previous section, we observed three types of sessions when the students interacted with the AI-powered hint system. We categorized a session as positive when the student ended the trajectory by clicking Accept Hint, as the tool is working as intended. We categorized a session as negative if the student received an error message or they themselves canceled the hint. Interestingly, the students still persevered in trying for new hints despite errors. 

Finally, we categorized sessions as neutral when the student clearly saw the hint but did not end the session like in positive or negative sessions. This means they saw the hint, but did not apply it to their code; for this reason, we do not consider these sessions negative or unsuccessful. Our analysis shows that neutral sessions make up 40% of all sessions, and we were curious about what exactly the students’ strategies were. 

In order to learn more about what the students were thinking, we selected a subset of the participants so that they represented different experience levels, as well as locations. We conducted six semi-structured interviews, each lasting approximately 30 minutes. The interviews had two main sections: asking the participants about their experience with AI-powered hints and about any other forms of assistance they might have used while completing the tasks.

Let’s look at what is going on in the neutral scenarios. We will focus on reporting two kinds of behaviors in these neutral scenarios: selective use of hints and combining partial solutions.

Selective use of hints

From these interviews, we learned that some participants chose to work with the code hints manually instead of clicking Accept Hint. The reasons for this depended on the situation: sometimes they chose to manually alter their code for improved learning, i.e. so that they could better remember how to fix the problem next time; sometimes they only wanted to use part of the code hint. We call this behavior selective use of hints.

In the log data, we could see that the students opened the visual diff window in the code editor. This window allows the student to view both their own code and the code hint. After opening this window, the students then manually typed in the recommended code from the hint – if the hint was clear. 

We can show you two examples where the hints were unclear. In the first, the text hint suggested that an image should be trimmed before applying filters, and that this trimming could be done by storing a temporary result in a variable. This can be seen in the image below, inside the box bordered by a dotted line.

As can be seen above, the student did not completely follow the hint; instead they only trimmed the image and did not create a new variable. An advantage of the in-IDE format over a course in an online editor is that the IDE will show the student an optimized way to proceed with the removed variable, even though the hint had suggested otherwise. The student’s strategy in this example suggests that students are not unquestioningly accepting hints, but are actually analyzing and adapting them for their benefit

In a second case, the text hint suggested that the student should add a Boolean variable to store the result of a game. The image below displays the hint, as well as the various student attempts, which included a few compilation errors. 

The text hint was not wrong, but it did not provide the full story: in Kotlin, Boolean variables can be initialized later when used in a loop, and so it provided the code without any initial value. Based on the student’s attempts to compile, they did not understand this language-specific feature. 

From looking more closely at the students’ behavior concerning selective use of hints, we learned both that students are actively trying to adapt the hints so that they can learn better and that, in creating the hints, we should take into account language-specific features that a beginner student might not yet know about. Both takeaways form a solid basis for future investigations. 

Combining partial solutions

In the qualitative analysis of the AI-powered hints tool, we additionally learned that the students sometimes combined information from multiple hint attempts, instead of using only one hint. In the log data, this appeared as clusters of closely spaced hint requests on the same portion of code, followed by students editing the solution themselves rather than just copy-and-pasting code. This behavior is depicted schematically in the image below.

This behavior, which we call combining partial solutions, can be seen as a strategy to develop a working solution by troubleshooting and making small changes to their solutions, each time generating a new hint. As with the previous behavior, students did not accept the code hint, which is why we categorized it as a neutral and not a positive scenario. 

Within this analysis, we found that students often toggled between their original code version and multiple hints: they kept the previous code hint windows open. We did not expect this behavior from students using the hint tool and will investigate further in future work.

We also observed that students sometimes copied their original code outside the task environment, reusing it later alongside new attempts. In the end, they were able to obtain a working solution with this strategy. This is another behavior that we did not expect from the students, and we are interested in investigating it further in the future. More specifically, we would like to provide the participants with multiple hints simultaneously (instead of sequentially), so that they can construct a working solution efficiently and effectively. 

From this analysis, we have learned that the hint tool could benefit from better support for partial adoption of hints and designs that embrace the way students naturally mix system suggestions with their own understanding and ideas, so that the student does not have to manually open multiple windows or copy-and-paste code from outside the IDE.

Furthermore, what we can understand from this behavior of combining partial solutions is that the students are active problem solvers. That is, they are not just clicking through the hints so that they can advance to the next step or lesson. Instead, students analyze the hints and adapt them to get even a better solution – critical thinking skills like these are invaluable in the LLM era.

Explore it yourself 

Interested in learning programming with the help of our AI-powered hints tool? Check out our JetBrains Academy course catalog.

You can also use our data to do your own research to discover something new about how students use smart feedback in online programming courses. 

Explore our dataset

And finally, if you’re interested in collaborating with our team, let us know!