Archive for the ‘en-US’ Category
[DotAI2024] DotAI 2024: Marcin Detyniecki – Navigating Bias Toward Equitable AI Outcomes
Marcin Detyniecki, Group Chief Data Scientist and Head of AI Research at AXA, probed the ethical frontiers of artificial intelligence at DotAI 2024. Steering AXA’s R&D toward fair, interpretable ML amid insurance’s high-stakes decisions, Detyniecki dissected algorithmic bias through predictive justice lenses. His exploration grappled with AI’s paradoxical promise: a “black box” oracle that, if harnessed judiciously, could forge impartial futures despite inherent opacity.
Unmasking Inherent Prejudices in Decision Engines
Detyniecki commenced with COMPAS, a U.S. recidivism predictor that flagged disproportionate risks for Black defendants, igniting bias debates. Yet, he challenged snap judgments: human intuitions, too, falter—his own unease at a “shady” visage mirroring the tool’s contested outputs. This duality reveals bias as endemic, not algorithmic anomaly; data mirrors societal skews, amplifying inequities unless confronted.
In insurance, parallels abound: pricing models risk entrenching disparities by correlating proxies like zip codes with peril, sidelining root causes. Detyniecki advocated reconstructing “sensitive variables”—demographics or vulnerabilities—within models to enforce equity, inverting the blind-justice archetype. Justice, he posited, demands vigilant oversight, not ignorance, to calibrate decisions across strata.
Fairness metrics proliferate—demographic parity, equalized odds—yet clash irreconcilably: precision for individuals versus solidarity in groups. Detyniecki’s Fairness Compass, an open GitHub toolkit, simulates trade-offs, logging rationales for transparency. This framework recasts metrics as tunable dials, enabling stakeholders to align algorithms with values, be it meritocracy or diversity.
Architecting Transparent Pathways to Just Applications
Detyniecki unveiled AXA’s causal architectures, embedding interventions to disentangle correlations from causations. By modeling “what-ifs”—altering features sans sensitive ties—models simulate equitable scenarios, outperforming ad-hoc debiasing. In hiring analogies, this yields top talent sans gender skew; in premiums, it mutualizes risks across cohorts, balancing acuity with solidarity.
Challenges persist: metric incompatibility demands philosophical reckoning, and sensitive data access invites misuse. Detyniecki urged guarded stewardship—reconstructing attributes internally to audit without exposure—ensuring AI amplifies equity, not erodes it.
Ultimately, Detyniecki affirmed AI’s redemptive arc: though veiled, its levers, when pulled ethically, illuminate fairer horizons. Trust, he concluded, bridges the chasm—humans guiding machines toward benevolence.
Links:
[SpringIO2024] Continuations: The Magic Behind Virtual Threads in Java by Balkrishna Rawool @ Spring I/O 2024
At Spring I/O 2024 in Barcelona, Balkrishna Rawool, a software engineer at ING Bank, captivated attendees with an in-depth exploration of continuations, the underlying mechanism powering Java’s virtual threads. Introduced as a final feature in Java 21 under Project Loom, virtual threads promise unprecedented scalability for Java applications. Balkrishna’s session demystified how continuations enable this scalability by allowing programs to pause and resume execution, offering a deep dive into their mechanics and practical applications.
Understanding Virtual Threads
Virtual threads, a cornerstone of Project Loom, are lightweight user threads designed to enhance scalability in Java applications. Unlike platform threads, which map directly to operating system threads and are resource-intensive, virtual threads require minimal memory, enabling developers to create millions without significant overhead. Balkrishna illustrated this by comparing platform threads, often pooled due to their cost, to virtual threads, which are created and discarded as needed, avoiding pooling anti-patterns. He emphasized that virtual threads rely on platform threads—termed carrier threads—for execution, with a scheduler mounting and unmounting them dynamically. This mechanism ensures efficient CPU utilization, particularly in I/O-bound applications where threads spend considerable time waiting, thus boosting scalability.
The Power of Continuations
Continuations, the core focus of Balkrishna’s talk, are objects that represent a program’s current state or the “rest” of its computation. They allow developers to pause a program’s execution and resume it later, a capability critical to virtual threads’ efficiency. Using Java’s Continuation API, Balkrishna demonstrated how continuations pause execution via the yield method, transferring control back to the caller, and resume via the run method. He showcased this with a simple example where a continuation printed values, paused at specific points, and resumed, highlighting the manipulation of the call stack to achieve this control transfer. Although the Continuation API is not intended for direct application use, understanding it provides insight into virtual threads’ behavior and scalability.
Building a Generator with Continuations
To illustrate continuations’ versatility, Balkrishna implemented a generator—a data structure yielding values lazily—using only the Continuation API, eschewing Java’s streams or iterators. Generators are ideal for resource-intensive computations, producing values only when needed. In his demo, Balkrishna created a generator yielding strings (“a,” “b,” “c”) by defining a Source object to handle value yields and pauses via continuations. The generator paused after each yield, allowing consumers to iterate over values in a loop, demonstrating how continuations enable flexible control flow beyond virtual threads, applicable to constructs like coroutines or exception handling.
Crafting a Simple Virtual Thread
In the session’s climax, Balkrishna guided attendees through implementing a simplified virtual thread class using continuations. The custom virtual thread paused execution during blocking operations, freeing platform threads, and supported a many-to-many relationship with carrier threads. He introduced a scheduler to manage virtual threads on a fixed pool of platform threads, using a queue for first-in-first-out scheduling. A demo with thousands of virtual threads, each simulating blocking calls, outperformed an equivalent platform-thread implementation, underscoring virtual threads’ scalability. By leveraging scoped values and timers, Balkrishna ensured accurate thread identification and resumption, providing a clear, hands-on understanding of virtual threads’ mechanics.
Links:
[PyConUS 2024] Demystifying Python Decorators: A Comprehensive Tutorial
At PyConUS2024, Reuven M. Lerner, an esteemed independent trainer and consultant from Lerner Consulting, presented an exhaustive tutorial titled “All About Decorators.” This session aimed to strip away the perceived complexity surrounding Python’s decorators, revealing their inherent power and versatility. Reuven’s approach was to guide attendees through the fundamental principles, practical applications, and advanced techniques of decorators, empowering developers to leverage this elegant feature for cleaner, more maintainable code. The tutorial offered a deep dive into what decorators are, their internal mechanics, how to construct them, and when to employ them effectively in various programming scenarios.
Functions as First-Class Citizens: The Foundation of Decorators
At the heart of Python’s decorator mechanism lies the concept of functions as first-class objects. Reuven Lerner began by elucidating this foundational principle, demonstrating how functions in Python are not merely blocks of code but entities that can be assigned to variables, passed as arguments to other functions, and returned as values from functions. This flexibility is pivotal, as it allows for the dynamic manipulation and extension of code behavior without altering the original function definition.
He illustrated this with simple examples, such as wrapping print statements with additional lines of text. Initially, this might involve manually calling a “wrapper” function that takes another function as an argument. This manual wrapping, while functional, quickly becomes cumbersome when applied repeatedly across numerous functions. Reuven showed how this initial approach, though verbose, laid the groundwork for understanding the more sophisticated decorator syntax. The ability to treat functions like any other data type in Python empowers developers to create highly modular and adaptable code structures, a cornerstone for building robust and scalable applications.
The Power of Closures: Functions Returning Functions
Building upon the concept of first-class functions, Reuven delved into the powerful notion of closures. A closure is a function that remembers the environment in which it was created, even after the outer function has finished executing. This is achieved when an inner function is defined within an outer function, and the outer function returns this inner function. The inner function retains access to the outer function’s local variables, forming a “closure” over that environment.
Lerner’s explanations made it clear that closures are a critical stepping stone to understanding how decorators work. The decorator pattern fundamentally relies on an outer function (the decorator) that takes a function as input, defines an inner “wrapper” function, and then returns this wrapper. This wrapper function “closes over” the original function and any variables from the decorator’s scope, allowing it to execute the original function while adding pre- or post-processing logic. This concept is essential for functions that need to maintain state or access context from their creation environment, paving the way for more sophisticated decorator implementations.
Implementing the Decorator Pattern Manually
Before introducing Python’s syntactic sugar for decorators, Reuven walked attendees through the manual implementation of the decorator pattern. This hands-on exercise was crucial for demystifying the @ syntax and showing precisely what happens under the hood. The manual approach involves explicitly defining a “decorator function” that accepts another function (the “decorated function”) as an argument. Inside the decorator function, a new “wrapper function” is defined. This wrapper function contains the additional logic to be executed before or after the decorated function, and it also calls the decorated function. Finally, the decorator function returns this wrapper.
The key step, as Reuven demonstrated, is then reassigning the original function’s name to the returned wrapper function. For instance, my_function = decorator(my_function). This reassignment effectively replaces the original my_function with the new, enhanced wrapper function, without changing how my_function is called elsewhere in the code. This explicit, step-by-step construction revealed the modularity and power of decorators, highlighting how they can seamlessly inject new behavior into existing functions while preserving their interfaces. Understanding this manual process is fundamental to debugging and truly mastering decorator usage.
Python’s Syntactic Sugar: The @ Operator
Once the manual mechanics of decorators were firmly established, Reuven introduced Python’s elegant and widely adopted @ syntax. This syntactic sugar simplifies the application of decorators significantly, making code more readable and concise. Instead of the explicit reassignment, my_function = decorator(my_function), the @ symbol allows developers to place the decorator name directly above the function definition:
@decorator
def my_function():
#### ...
Lerner emphasized that this @ notation is merely a convenience for the manual wrapping process discussed earlier. It performs the exact same operation of passing my_function to decorator and reassigning the result back to my_function. This clarity was vital, as many developers initially find the @ syntax magical. Reuven illustrated how this streamlined syntax enhances code readability, especially when multiple decorators are applied to a single function, or when creating custom decorators for specific tasks. The @ operator makes decorators a powerful and expressive tool in the Python developer’s toolkit, promoting a clean separation of concerns and encouraging reusable code patterns.
Practical Applications of Decorators
The tutorial progressed into a series of practical examples, showcasing the diverse utility of decorators in real-world scenarios. Reuven presented various use cases, from simple enhancements to more complex functionalities:
- “Shouter” Decorator: A classic example where a decorator modifies the output of a function, perhaps by converting it to uppercase or adding exclamation marks. This demonstrates how decorators can alter the result returned by a function.
- Timing Function Execution: A highly practical application involves using a decorator to measure the execution time of a function. This is invaluable for performance profiling and identifying bottlenecks in code. The decorator would record the start time, execute the function, record the end time, and then print the duration, all without cluttering the original function’s logic.
- Input and Output Validation: Decorators can be used to enforce constraints on function arguments or to validate the return value. For instance, a decorator could ensure that a function only receives positive integers or that its output adheres to a specific format. This promotes data integrity and reduces errors.
- Logging and Authentication: More advanced applications include decorators for logging function calls, handling authentication checks before a function executes, or implementing caching mechanisms to store and retrieve previously computed results.
Through these varied examples, Reuven underscored that decorators are not just an academic curiosity but a powerful tool for injecting cross-cutting concerns (like logging, timing, validation) into functions in a clean, non-intrusive manner. This approach adheres to the “separation of concerns” principle, making code more modular, readable, and easier to maintain.
Decorators with Arguments and Stacking Decorators
Reuven further expanded the attendees’ understanding by demonstrating how to create decorators that accept arguments. This adds another layer of flexibility, allowing decorators to be configured at the time of their application. To achieve this, an outer function is required that takes the decorator’s arguments and then returns the actual decorator function. This creates a triple-nested function structure, where the outermost function handles arguments, the middle function is the actual decorator that takes the decorated function, and the innermost function is the wrapper.
He also covered the concept of “stacking decorators,” where multiple decorators are applied to a single function. When decorators are stacked, they are executed from the bottom up (closest to the function definition) to the top (furthest from the function definition). Each decorator wraps the function that results from the application of the decorator below it. This allows for the sequential application of various functionalities to a single function, building up complex behaviors from smaller, modular units. Reuven carefully explained the order of execution and how the output of one decorator serves as the input for the next, providing a clear mental model for understanding chained decorator behavior.
Preserving Metadata with functools.wraps
A common side effect of using decorators is the loss of the decorated function’s original metadata, such as its name (__name__), docstring (__doc__), and module (__module__). When a decorator replaces the original function with its wrapper, the metadata of the wrapper function is what becomes visible. This can complicate debugging, introspection, and documentation.
Reuven introduced functools.wraps as the standard solution to this problem. functools.wraps is itself a decorator that can be applied to the wrapper function within your custom decorator. When used, it copies the relevant metadata from the original function to the wrapper function, effectively “wrapping” the metadata along with the code.
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
##### ... decorator logic ...
return func(*args, **kwargs)
return wrapper
This simple yet crucial addition ensures that decorated functions retain their original identity and documentation, making them behave more like their undecorated counterparts. Reuven stressed the importance of using functools.wraps in all custom decorators to avoid unexpected behavior and maintain code clarity, a best practice for any Python developer working with decorators.
Extending Decorator Concepts: Classes as Decorators and Decorating Classes
Towards the end of the tutorial, Reuven touched upon more advanced decorator patterns, including the use of classes as decorators and the application of decorators to classes themselves.
- Classes as Decorators: While functions are the most common way to define decorators, classes can also serve as decorators. This is achieved by implementing the
__call__method in the class, making instances of the class callable. The__init__method typically takes the function to be decorated, and the__call__method acts as the wrapper, executing the decorated function along with any additional logic. This approach can be useful for decorators that need to maintain complex state or have more intricate setup/teardown procedures. - Decorating Classes: Decorators can also be applied to classes, similar to how they are applied to functions. When a class is decorated, the decorator receives the class object itself as an argument. The decorator can then modify the class, for example, by adding new methods, altering existing ones, or registering the class in some way. This is often used in frameworks for tasks like dependency injection, ORM mapping, or automatically adding mixins.
Reuven’s discussion of these more advanced scenarios demonstrated the full breadth of decorator applicability, showcasing how this powerful feature can be adapted to various architectural patterns and design needs within Python programming. This segment provided a glimpse into how decorators extend beyond simple function wrapping to influence the structure and behavior of entire classes, offering a flexible mechanism for meta-programming.
Links:
- Reuven M. Lerner on LinkedIn
- Reuven M. Lerner’s Professional Website
- LernerPython Website (Courses)
- Video URL
Hashtags: #Python #Decorators #PyConUS2024 #Programming #SoftwareDevelopment #Functions #Closures #PythonTricks #CodeQuality #ReuvenMLerner #LernerConsulting #LernerPython
Onyxia: A User-Centric Interface for Data Scientists in the Cloud Age
Introduction
The team from INSEE presents Onyxia, an open-source, Kubernetes-based platform designed to offer flexible, collaborative, and powerful cloud environments for data scientists.
Rethinking Data Science Infrastructure
Traditional local development faces issues like configuration divergence, data duplication, and limited compute resources. Onyxia solves these by offering isolated namespaces, integrated object storage, and a seamless user interface that abstracts Kubernetes and S3 complexities.
Versatile Deployment
With a few clicks, users can launch preconfigured environments — including Jupyter notebooks, VS Code, Postgres, and MLflow — empowering fast innovation without heavy IT overhead. Organizations can extend Onyxia by adding custom services, ensuring future-proof, evolvable data labs.
Success Stories
Adopted across French universities and research labs, Onyxia enables students and professionals alike to work in secure, scalable, and fully-featured environments without managing infrastructure manually.
Conclusion
Onyxia democratizes access to powerful cloud tools for data scientists, streamlining collaboration and fostering innovation.
[NDCOslo2024] Reusable Ideas About the Reuse of Software – Audun Fauchald Strand & Trond Arve Wasskog
In the sprawling digital expanse of Norway’s welfare agency, NAV, where 143 million lines of code burgeon, Audun Fauchald Strand and Trond Arve Wasskog, principal engineers, confront the Sisyphean challenge of maintenance. Their discourse, a clarion call for strategic reuse, dissects NAV’s labyrinthine codebase, advocating for shared components to curb redundancy. With a nod to domain-driven design and Conway’s Law, Audun and Trond weave a narrative of organizational alignment, technical finesse, and cultural recalibration, urging a shift from ad-hoc replication to deliberate commonality.
NAV, serving Norway’s social safety net, grapples with legacy sprawl. Audun and Trond, seasoned navigators of this terrain, challenge the mantra “reuse should be discovered, not designed.” Their thesis: intentional reuse, underpinned by product thinking, demands ownership, incentives, and architecture harmonized with organizational contours. From open-source libraries to shared services, they map a spectrum of reuse, balancing technical feasibility with social dynamics.
Redefining Reuse: From Code to Culture
Reuse begins with understanding context. Audun outlines NAV’s scale: thousands of developers, hundreds of teams, and a codebase ballooning through modernization. Copy-pasting code—tempting for speed—breeds technical debt. Instead, they champion shared libraries and services, like payment gateways or journaling systems, already reused across NAV’s ecosystem. Open-source, they note, exemplifies external success; internally, however, reuse falters without clear ownership.
Trond delves into Conway’s Law: systems mirror organizational structures. NAV’s fragmented teams spawn siloed solutions unless guided by unified governance. Their solution: designate component owners, aligning incentives to prioritize maintenance over novelty. A payment service, reused across domains, exemplifies success, reducing duplication while enhancing reliability.
Technical Tactics and Organizational Orchestration
Technically, reuse demands robust infrastructure. Audun advocates platforms—centralized APIs, standardized pipelines—to streamline integration. Shared libraries, versioned meticulously, prevent divergence, while microservices enable modular reuse. Yet, technical prowess alone suffices not; social engineering is paramount. Trond emphasizes cross-team collaboration, ensuring components like letter-sending services are maintained by dedicated squads, not orphaned.
Their lesson: reuse is a socio-technical dance. Without organizational buy-in—financing, accountability, clear roles—components decay. NAV’s pivot to product-oriented teams, guided by domain-driven design, fosters reusable assets, aligning technical solutions with business imperatives.
Navigating Pitfalls: Ownership and Maintenance
The core challenge lies in the “blue box”—NAV’s monolithic systems. Audun and Trond dissect failures: reused components falter when unowned, leading to outages or obsolescence. Their antidote: explicit ownership models, where teams steward components, supported by funding and metrics. They cite successes—journaling services, payment APIs—where ownership ensures longevity.
Their vision: an internal open-source ethos, where teams contribute to and consume shared assets, mirrored by external triumphs like Kubernetes. By realigning incentives, NAV aims to transform reuse from serendipity to strategy, reducing code bloat while accelerating delivery.
Fostering a Reuse-First Mindset
Audun and Trond conclude with a cultural clarion: reuse thrives on intentionality. Teams must evaluate trade-offs—forking versus libraries, services versus platforms—within their context. Their call to action: join NAV’s mission, where reuse reshapes welfare delivery, blending technical rigor with societal impact.
Links:
Renovate/Dependabot: How to Take Control of Dependency Updates
At Devoxx France 2024, held in April at the Palais des Congrès in Paris, Jean-Philippe Baconnais and Lise Quesnel, consultants at Zenika, presented a 30-minute talk titled Renovate/Dependabot, ou comment reprendre le contrôle sur la mise à jour de ses dépendances. The session explored how tools like Dependabot and Renovate automate dependency updates, reducing the tedious and error-prone manual process. Through a demo and lessons from open-source and client projects, they shared practical tips for implementing Renovate, highlighting its benefits and pitfalls. 🚀
The Pain of Dependency Updates
The talk opened with a relatable skit: Lise, working on a side project (a simple Angular 6 app showcasing women in tech), admitted to neglecting updates due to the effort involved. Jean-Philippe emphasized that this is a common issue across projects, especially in microservice architectures with numerous components. Updating dependencies is critical for:
- Security: Applying patches to reduce exploitable vulnerabilities.
- Features: Accessing new functionalities.
- Bug Fixes: Benefiting from the latest corrections.
- Performance: Leveraging optimizations.
- Attractiveness: Using modern tech stacks (e.g., Node 20 vs. Node 8) to appeal to developers.
However, the process is tedious, repetitive, and complex due to transitive dependencies (e.g., a median of 683 for NPM projects) and cascading updates, where one update triggers others.
Automating with Dependabot and Renovate
Dependabot (acquired by GitHub) and Renovate (from Mend) address this by scanning project files (e.g., package.json, Maven POM, Dockerfiles) and opening pull requests (PRs) or merge requests (MRs) for available updates. These tools:
- Check registries (NPM, Maven Central, Docker Hub) for new versions.
- Provide visibility into dependency status.
- Save time by automating version checks, especially in microservice setups.
- Enhance reactivity, critical for applying security patches quickly.
Setting Up the Tools
Dependabot: Configured via a dependabot.yml file, specifying ecosystems (e.g., NPM), directories, and update schedules (e.g., weekly). On GitHub, it integrates natively via project settings. GitLab users can use a similar approach.
# dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
Renovate: Configured via a renovate.json file, extending default presets. It supports GitHub and GitLab via apps or CI/CD pipelines (e.g., GitLab CI with a Docker image). For self-hosted setups, Renovate can run as a Docker container or Kubernetes CronJob.
# renovate.json
{
"extends": [
"config:recommended"
]
}
In their demo, Jean-Philippe and Lise showcased Renovate on a GitLab project, using a .gitlab-ci.yml pipeline to run Renovate on a schedule, creating MRs for updates like rxjs (from 6.3.2 to 6.6.7).
Customizing Renovate
Renovate’s strength lies in its flexibility through presets and custom configurations:
- Presets: Predefined rules (e.g.,
npm:unpublishSafewaits 3 days before proposing updates). Presets can extend others, forming a hierarchy (e.g.,config:recommendedextends base presets). - Custom Presets: Organizations can define reusable configs in a dedicated repository (e.g.,
renovate-config) and apply them across projects.
// renovate-config/default.json
{
"extends": [
"config:recommended",
":npm"
]
}
- Grouping Updates: Combine related updates (e.g., all ESLint packages) using
packageRulesor presets likegroup:recommendedLintersto reduce PR noise.
{
"packageRules": [
{
"matchPackagePatterns": ["^eslint"],
"groupName": "eslint packages"
}
]
}
- Dependency Dashboard: An issue tracking open, rate-limited, or ignored MRs, activated via the
dependencyDashboardfield or preset.
Going Further: Automerge and Beyond
To streamline updates, Renovate supports automerge, automatically merging MRs if the pipeline passes, relying on robust tests. Options include:
automerge: truefor all updates.automergeType: "pr"orstrategyfor specific behaviors.- Presets like
automerge:patchfor patch updates only.
The demo showed an automerged rxjs update, triggering a new release (v1.2.1) via semantic-release, tagged, and deployed to Google Cloud Run. A failed Angular update (due to a major version gap) demonstrated how failing tests block automerge, ensuring safety.
Renovate can also update itself and its configuration (e.g., deprecated fields) via the config:migration preset, creating MRs for self-updates.
Lessons Learned and Recommendations
From their experiences, Jean-Philippe and Lise shared key tips:
- Manage PR Overload: Limit concurrent PRs (e.g.,
prConcurrentLimit: 5) and group related updates to reduce noise. - Use Schedules: Run Renovate at off-peak times (e.g., nightly) to avoid overloading CI runners and impacting production deployments.
- Ensure Robust Tests: Automerge relies on trustworthy tests; weak test coverage can lead to broken builds.
- Balance Frequency: Frequent runs catch updates quickly but risk conflicts; infrequent runs may miss critical patches.
- Monitor Resource Usage: Excessive pipelines can strain runners and increase costs in autoscaling environments (e.g., cloud platforms).
- Handle Transitive Dependencies: Renovate manages them like direct dependencies, but cascading updates require careful review.
- Support Diverse Ecosystems: Renovate works well with Java (e.g., Spring Boot, Quarkus), Scala, and NPM, with grouping to manage high-dependency ecosystems like NPM.
- Internal Repositories: Configure Renovate to scan private registries by specifying URLs.
- Major Updates: Use presets to stage major updates incrementally, avoiding risky automerge for breaking changes.
Takeaways
Jean-Philippe and Lise’s talk highlighted how Dependabot and Renovate transform dependency management from a chore to a streamlined process. Their demo and practical advice showed how Renovate’s flexibility—via presets, automerge, and dashboards—empowers teams to stay secure and up-to-date, especially in complex microservice environments. However, success requires careful configuration, robust testing, and resource management to avoid overwhelming teams or infrastructure. 🌟
[DevoxxUK2024] Project Leyden: Capturing Lightning in a Bottle by Per Minborg
Per Minborg, a seasoned member of Oracle’s Core Library team, delivered an insightful session at DevoxxUK2024, unveiling the ambitions of Project Leyden, a transformative initiative to enhance Java application performance. Focused on slashing startup time, accelerating warmup, and reducing memory footprint, Per’s talk explores how Java can evolve to meet modern demands while preserving its dynamic nature. By strategically shifting computations to optimize execution, Project Leyden introduces innovative techniques like condensers and enhanced Class Data Sharing (CDS). This session provides a roadmap for developers seeking to harness Java’s potential in high-performance environments, balancing flexibility with efficiency.
The Vision of Project Leyden
Per begins by outlining the core objectives of Project Leyden: improving startup time, warmup time, and memory footprint. Startup time, the duration from launching an application to its first meaningful output (e.g., a “Hello World” or serving a web request), is critical for user experience. Warmup time, the period until an application reaches peak performance through JIT compilation, can hinder responsiveness in dynamic systems. Footprint, encompassing memory and storage use, impacts scalability, especially in cloud environments. Per emphasizes that the best approach is to eliminate unnecessary computations, but when that’s not feasible, shifting them temporally—either earlier to compile time or later to runtime—can yield significant gains. This philosophy underpins Leyden’s strategy to refine Java’s execution model.
Shifting Computations for Efficiency
A cornerstone of Project Leyden is the concept of temporal computation shifting. Per explains that Java’s dynamic nature—encompassing dynamic class loading, JIT compilation, and runtime optimizations—enables expressive programming but can inflate startup and warmup times. By moving computations to build time, such as through constant folding or ahead-of-time (AOT) compilation, Leyden reduces runtime overhead. Alternatively, lazy evaluation postpones non-critical tasks, streamlining startup. Per introduces condensers, a novel mechanism that transforms program representations by shifting computations earlier, adding metadata, or imposing constraints on dynamism. Condensers are composable, meaning-preserving, and selectable, allowing developers to tailor optimizations based on application needs. For instance, a condenser might precompile lambda expressions into bytecode at build time, slashing runtime costs.
Enhancing Class Data Sharing (CDS)
Per delves into Class Data Sharing (CDS), a long-standing Java feature that Project Leyden enhances to achieve dramatic performance boosts. CDS allows pre-initialized JDK classes to be stored in a file, bypassing costly class loading during startup. With CDS++, Leyden extends this to include application classes, compiled code, and resolved constant pool references. Per shares compelling benchmarks: a test compiling 100 small Java files achieved a 2x startup improvement, while an XML parsing workload saw an 8x boost. For the Spring Pet Clinic benchmark, Leyden’s optimizations, including early class loading and cached compiled code, yielded up to 4x faster startup. These gains stem from a training run approach, where a representative execution gathers profiling data to inform optimizations, ensuring compatibility across platforms.
Balancing Dynamism and Performance
Java’s dynamism—encompassing dynamic typing, class loading, and reflection—empowers developers but complicates optimization. Per proposes selective constraints to balance this trade-off. For example, developers can restrict dynamic class loading for specific modules, enabling aggressive optimizations without sacrificing Java’s flexibility. The stable value feature, initially part of Leyden but now a standalone JEP, allows delayed initialization of final fields while maintaining performance akin to compile-time constants. Per illustrates this with a Fibonacci computation example, where memoization using stable values drastically reduces recursive overhead. By offering a “mixer board” of concessions, Leyden empowers developers to fine-tune performance, ensuring compatibility and preserving program semantics across diverse use cases.
Links:
[NodeCongress2024] Asynchronous Context Tracking in Modern JavaScript Runtimes: From `AsyncLocalStorage` to the `AsyncContext` Standard
Lecturer: James M Snell
James M Snell is a distinguished open-source contributor and software engineer, currently serving as a Principal Engineer on the Cloudflare Workers team. He is a long-standing core contributor to the Node.js Technical Steering Committee (TSC), where his technical leadership has been instrumental in modernizing Node.js’s networking stack, including the implementation of HTTP/2, the WHATWG URL implementation, and the QUIC protocol. Snell is also a key founder and participant in the WinterCG (Web-interoperable Runtimes Community Group), an effort dedicated to aligning standards across disparate JavaScript runtimes.
- Institutional Profile: James M Snell – The Cloudflare Blog
- GitHub: James M Snell jasnell – GitHub
Abstract
This article provides an analytical deep dive into the concept and implementation of Asynchronous Context Tracking in JavaScript runtimes, focusing on Node.js’s existing AsyncLocalStorage (ALS) API and the proposed AsyncContext standard. It explains the critical problem of preserving request-specific contextual data (e.g., request IDs or transaction details) across asynchronous I/O boundaries in highly concurrent environments. The article details the technical methodology, which relies on Async Hooks and a Context Frame Stack, and discusses the implications of the TC-39 standardization effort to create a portable, globally accessible AsyncContext API across runtimes like Node.js, Cloudflare Workers, Deno, and Bun.
Context: The Challenge of Asynchronous Execution Flow
In a concurrent, non-blocking I/O model like Node.js, the execution of a single logical operation (e.g., handling one HTTP request) is typically fragmented across multiple asynchronous callbacks. The JavaScript engine often switches between different logical requests while waiting for I/O operations to complete, making it impossible to rely on simple global or thread-local variables for storing request-specific metadata. The challenge is ensuring that contextual information (such as a unique request identifier or security principal) is preserved and accessible to every segment of the logical operation’s flow, regardless of how many other concurrent operations interleave with it.
Methodology: Context Frames and Async Hooks
Asynchronous Context Tracking solves this by establishing a mechanism to associate a context frame (a logical map of key/value pairs) with the execution flow of an asynchronous operation.
- The Role of Async Hooks: The foundation of this system is the Async Hook API (or its internal equivalent in other runtimes). The runtime uses these hooks to trace the lifecycle of asynchronous resources (e.g., timers, network requests). Every time an asynchronous operation is created or executed, the runtime utilizes the hooks to push and pop context frames onto a dedicated stack for that specific asynchronous flow.
- The
runandgetStore/getMethods: The primary interface for managing context is therunmethod (available on bothAsyncLocalStorageandAsyncContext). When a function is wrapped instore.run(value, callback), it initiates a new context frame containing thatvalue, ensuring that all subsequent asynchronous operations originating from the callback have access to the frame. ThegetStore(ALS) orget(Async Context) method then accesses the value from the current frame on the stack. - Copy-on-Run Principle: Critically, the
runmethod ensures that context is copied and isolated for the new frame. Modifying a context value within aruncall does not affect the context of the calling function, preventing data leakage or corruption between concurrent requests.
The Evolution to AsyncContext and Interoperability
The AsyncLocalStorage API in Node.js, initially residing in node:async_hooks, has proven the utility of this model, leading to its adoption in other runtimes. The subsequent step is the standardization of AsyncContext by the TC-39 committee. The changes between the two APIs are minimal—primarily making the API a global object and renaming getStore to get—but the implications are profound. The standardization effort ensures that this crucial pattern for context propagation becomes portable and interoperable across the entire JavaScript ecosystem, benefiting Node.js, Cloudflare Workers, Deno, and Bun.
Links
- Lecture Video: Understanding Async Context – James Snell, Node Congress 2024
- Lecturer’s GitHub: https://github.com/jasnell
- Organization Blog: Cloudflare Blog
Hashtags: #AsyncContext #AsyncLocalStorage #NodeJS #JavaScriptRuntimes #AsyncHooks #WinterCG #TC39
[DevoxxGR2024] Small Steps Are the Fastest Way Forward: Navigating Chaos in Software Development
Sander Hoogendoorn, CTO at iBOOD, delivered an engaging and dynamic talk at Devoxx Greece 2024, addressing the challenges of software development in a rapidly changing world. Drawing from his extensive experience as a programmer, architect, and leader, Sander explored how organizations can overcome technical debt and the innovator’s dilemma by embracing continuous experimentation, small teams, and short delivery cycles. His narrative, peppered with real-world anecdotes, offered practical strategies for navigating complexity and fostering innovation in a post-agile landscape.
Understanding Technical Debt and Quality
Sander opened by tackling the elusive concept of software quality, contrasting it with tangible products like coffee or cars, where higher quality correlates with higher cost. In software, quality—encompassing maintainability, testability, and reliability—is harder to quantify and often lacks a direct price relationship. He introduced Ward Cunningham’s concept of technical debt, where initial shortcuts accelerate development but, if unaddressed, can cripple organizations. Sander shared an example from an insurance company with 18 million lines of COBOL and 12 million lines of Java, where outdated code and retiring developers created a maintenance nightmare. Similarly, at iBOOD, a patchwork of systems led to “technical death,” where maintenance consumed all resources, stifling innovation.
To mitigate technical debt, Sander advocated for continuous refactoring as part of daily work, rather than a separate task requiring approval. He emphasized finding a balance between quality and cost, tailored to the organization’s goals—whether building a quick mobile app or a long-lasting banking system.
The Innovator’s Dilemma and Continuous Renovation
Sander introduced the innovator’s dilemma, where successful products reach a saturation point, and new entrants with innovative technologies disrupt the market. He recounted his experience at a company that pioneered smart thermostats but failed to reinvent itself, leading to its acquisition and dissolution. To avoid this fate, organizations must operate in “continuous renovation mode,” maintaining existing systems while incrementally building new features. This approach, inspired by John Gall’s law—that complex systems evolve from simple, working ones—requires small, iterative steps rather than large-scale rebuilds.
At iBOOD, Sander implemented this by allocating 70% of resources to innovation and 30% to maintenance, ensuring the “shop stays open” while progressing toward strategic goals. He emphasized the importance of defining a clear “dot on the horizon,” such as iBOOD’s ambition to become Europe’s leading deal site, to guide these efforts.
Navigating Complexity with the Cynefin Framework
To navigate the chaotic and complex nature of modern software development, Sander introduced the Cynefin framework, which categorizes problems into clear, complicated, complex, and chaotic zones. Most software projects reside in the complex zone, where no best practices exist, and experimentation is essential. He cautioned against treating complex problems as complicated, citing failed attempts at iBOOD’s insurance client to rebuild systems from scratch. Instead, organizations should run small experiments, accepting the risk of failure as a path to learning.
Sander illustrated this with iBOOD’s decision-making process, where a cross-functional team evaluates ideas based on their alignment with strategic goals, feasibility, and size. Ideas too large are broken into smaller pieces, ensuring manageable experiments that deliver quick feedback.
Delivering Features in Short Cycles
Sander argued that traditional project-based approaches and even Scrum’s sprint model are outdated in a world demanding rapid iteration. He advocated for continuous delivery, where features are deployed multiple times daily, minimizing dependencies and enabling immediate feedback. At iBOOD, features are released in basic versions, refined based on business input, and prioritized over less critical tasks. This approach, supported by automated CI/CD pipelines and extensive testing, ensures quality is built into the process, reducing reliance on manual inspections.
He shared iBOOD’s pipeline, which includes unit tests, static code analysis, and production testing, allowing developers to code with confidence. By breaking features into small, independent services, iBOOD achieves flexibility and resilience, avoiding the pitfalls of monolithic systems.
Empowering Autonomous Micro-Teams
Finally, Sander addressed the human element of software development, arguing that the team, not the individual, is the smallest unit of delivery. He advocated for autonomous “micro-teams” that self-organize around tasks, drawing an analogy to jazz ensembles where musicians form sub-groups based on skills. At iBOOD, developers choose their tasks and collaborators, fostering learning and flexibility. This autonomy, while initially uncomfortable for some, encourages ownership and innovation.
Sander emphasized minimizing rules to promote critical thinking, citing an Amsterdam experiment where removing traffic signs improved road safety through communication. By eliminating Scrum rituals like sprints and retrospectives, iBOOD’s teams focus on solving one problem daily, enhancing efficiency and morale.
Conclusion
Sander Hoogendoorn’s talk at Devoxx Greece 2024 offered a refreshing perspective on thriving in software development’s chaotic landscape. By addressing technical debt, embracing the innovator’s dilemma, and leveraging the Cynefin framework, organizations can navigate complexity through small, experimental steps. Continuous delivery and autonomous micro-teams further empower teams to innovate rapidly and sustainably. Sander’s practical insights, grounded in his leadership at iBOOD, provide a compelling blueprint for organizations seeking to evolve in a post-agile world.
Links:
[GoogleIO2024] Developer Keynote: Innovations in AI and Development Tools at Google I/O 2024
The Developer Keynote at Google I/O 2024 showcased a transformative vision for software creation, emphasizing how generative artificial intelligence is reshaping the landscape for creators worldwide. Delivered by a team of Google experts, the session highlighted accessible AI models, enhanced productivity across platforms, and new tools designed to simplify complex workflows. This presentation underscored Google’s commitment to empowering millions of developers through an ecosystem that spans billions of devices, fostering innovation without the burden of underlying infrastructure challenges.
Advancing AI Accessibility and Model Integration
A core theme of the keynote revolved around making advanced AI capabilities available to every programmer. The speakers introduced Gemini 1.5 Flash, a lightweight yet powerful model optimized for speed and cost-effectiveness, now accessible globally via the Gemini API in Google AI Studio. This tool balances quality, efficiency, and affordability, enabling developers to experiment with multimodal applications that incorporate audio, video, and extensive context windows. For instance, Jacqueline demonstrated a personal workflow where voice memos and prior blog posts were synthesized into a draft article, illustrating how large context windows—up to two million tokens—unlock novel interactions while reducing computational expenses through features like context caching.
This approach extends beyond simple API calls, as the team emphasized techniques such as model tuning and system instructions to personalize outputs. Real-world examples included Loc.AI’s use of Gemini for renaming elements in frontend designs from Figma, enhancing code readability by interpreting nondescript labels. Similarly, Invision leverages the model’s speed for real-time environmental descriptions aiding low-vision users, while Zapier automates podcast editing by removing filler words from audio uploads. These cases highlight how Gemini empowers practical transformations, from efficiency gains to user delight, encouraging participation in the Gemini API developer competition for innovative applications.
Enhancing Mobile Development with Android and Gemini
Shifting focus to mobile ecosystems, the keynote delved into Android’s evolution as an AI-centric operating system. With over three billion devices, Android now integrates Gemini to enable on-device experiences that prioritize privacy and low latency. Gemini Nano, the most efficient model for edge computing, powers features like smart replies in messaging without data leaving the device, available on select hardware like the Pixel 8 Pro and Samsung Galaxy S24 series, with broader rollout planned.
Early adopters such as Patreon and Grammarly showcased its potential: Patreon for summarizing community chats, and Grammarly for intelligent suggestions. Maru elaborated on Kotlin Multiplatform support in Jetpack libraries, allowing shared business logic across Android, iOS, and web, as seen in Google Docs migrations. Compose advancements, including performance boosts and adaptive layouts, were highlighted, with examples from SoundCloud demonstrating faster UI development and cross-form-factor compatibility. Testing improvements, like Android Device Streaming via Firebase and resizable emulators, ensure robust validation for diverse hardware.
Jamal illustrated Gemini’s role in Android Studio, evolving from Studio Bot to provide code optimizations, translations, and multimodal inputs for rapid prototyping. A demo converted a wireframe image into functional Jetpack Compose code, underscoring how AI accelerates from ideation to implementation.
Revolutionizing Web and Cross-Platform Experiences
The web’s potential was amplified through AI integrations, marking its 35th anniversary with tools like WebGPU and WebAssembly for on-device inference. John discussed how these enable efficient model execution across devices, with examples like Bilibili’s 30% session duration increase via MediaPipe’s image recognition. Chrome’s enhancements, including AI-powered dev tools for error explanations and code suggestions, streamline debugging, as shown in a Boba tea app troubleshooting CORS issues.
Aaron introduced Project IDX, now in public beta, as an integrated workspace for full-stack, multiplatform development, incorporating Google Maps, DevTools, and soon Checks for privacy compliance. Flutter’s updates, including WebAssembly support for up to 2x performance gains, were exemplified by Bricket’s cross-platform expansion. Firebase’s evolution, with Data Connect for SQL integration, App Hosting for scalable web apps, and Genkit for seamless AI workflows, further simplifies backend connections.
Customizing AI Models and Future Prospects
Shabani and Lawrence explored open models like Gemma, with new variants such as PaliGemma for vision-language tasks and the upcoming Gemma 2 for enhanced performance on optimized hardware. A demo in Colab illustrated fine-tuning Gemma for personalized book recommendations, using synthetic data from Gemini and on-device inference via MediaPipe. Project Gameface’s Android expansion demonstrated accessibility advancements, while an early data science agent concept showcased multi-step reasoning with long context.
The keynote concluded with resources like accelerators and the Google Developer Program, emphasizing community-driven innovation. Eugene AI’s emissions reduction via DeepMind research exemplified real-world impact, reinforcing Google’s ecosystem for reaching global audiences.