Posts Tagged ‘SoftwareEngineering’
[DevoxxGR2025] Engineering for Social Impact
Giorgos Anagnostaki and Kostantinos Petropoulos, from IKnowHealth, delivered a concise 15-minute talk at Devoxx Greece 2025, portraying software engineering as a creative process with profound social impact, particularly in healthcare.
Engineering as Art
Anagnostaki likened software engineering to creating art, blending design and problem-solving to build functional systems from scratch. In healthcare, this creativity carries immense responsibility, as their work at IKnowHealth supports radiology departments. Their platform, built for Greece’s national imaging repository, enables precise diagnoses, like detecting cancer or brain tumors, directly impacting patients’ lives. This human connection fuels their motivation, transforming code into life-saving tools.
The Radiology Platform
Petropoulos detailed their cloud-based platform on Azure, connecting hospitals and citizens. Hospitals send DICOM imaging files and HL7 diagnosis data via VPN, while citizens access their medical history through a portal, eliminating CDs and printed reports. The system supports remote diagnosis and collaboration, allowing radiologists to share anonymized cases for second opinions, enhancing accuracy and speeding up critical decisions, especially in understaffed regions.
Technical Challenges
The platform handles 2.5 petabytes of imaging data annually from over 100 hospitals, requiring robust storage and fast retrieval. High throughput (up to 600 requests per minute per hospital) demands scalable infrastructure. Front-end challenges include rendering thousands of DICOM images without overloading browsers, while GDPR-compliant security ensures data privacy. Integration with national health systems added complexity, but the platform’s impact—illustrated by Anagnostaki’s personal story of his father’s cancer detection—underscores its value.
Links
[DevoxxBE2024] Mayday Mark 2! More Software Lessons From Aviation Disasters by Adele Carpenter
At Devoxx Belgium 2024, Adele Carpenter delivered a gripping follow-up to her earlier talk, diving deeper into the technical and human lessons from aviation disasters and their relevance to software engineering. With a focus on case studies like Air France 447, Copa Airlines 201, and British Midlands 92, Adele explored how system complexity, redundancy, and human factors like cognitive load and habituation can lead to catastrophic failures. Her session, packed with historical context and practical takeaways, highlighted how aviation’s century-long safety evolution offers critical insights for building robust, human-centric software systems.
The Evolution of Aviation Safety
Adele began by tracing the rapid rise of aviation from the Wright Brothers’ 1903 flight to the jet age, catalyzed by two world wars and followed by a 20% annual growth in commercial air traffic by the late 1940s. This rapid adoption led to a peak in crashes during the 1970s, with 230 fatal incidents, primarily due to pilot error, as shown in data from planecrashinfo.com. However, safety has since improved dramatically, with fatalities dropping to one per 10 million passengers by 2019. Key advancements, like Crew Resource Management (CRM) introduced after the 1978 United Airways 173 crash, reduced pilot-error incidents by enhancing cockpit communication. The 1990s and 2000s saw further gains through fly-by-wire technology, automation, and wind shear detection systems, making aviation a remarkable engineering success story.
The Perils of Redundancy and Complexity
Using Air France 447 (2009) as a case study, Adele illustrated how excessive redundancy can overwhelm users. The Airbus A330’s three pitot tubes, feeding airspeed data to multiple Air Data Inertial Reference Units (ADIRUs), failed due to icing, causing the autopilot to disconnect and bombard pilots with alerts. In alternate law, without anti-stall protection, the less-experienced pilot’s nose-up input led to a stall, exacerbated by conflicting control inputs in the dark cockpit. This cascade of failures—compounded by sensory overload and inadequate training—resulted in 228 deaths. Adele drew parallels to software, recounting an downtime incident at Trifork caused by a RabbitMQ cluster sync issue, highlighting how poorly understood redundancy can paralyze systems under pressure.
Deadly UX and Consistency Over Correctness
Copa Airlines 201 (1992) underscored the dangers of inconsistent user interfaces. A faulty captain’s vertical gyro fed bad data, disconnecting the autopilot. The pilots, trained on a simulator where a switch’s “left” position selected auxiliary data, inadvertently set both displays to the faulty gyro due to a reversed switch design in the actual Boeing 737. This “deadly UX” caused the plane to roll out of the sky, killing all aboard. Adele emphasized that consistency in design—over mere correctness—is critical in high-stakes systems, as it aligns with human cognitive limitations, reducing errors under stress.
Human Factors: Assumptions and Irrationality
British Midlands 92 (1989) highlighted how assumptions can derail decision-making. Experienced pilots, new to the 737-400, mistook smoke from a left engine fire for a right engine issue due to a design change in air conditioning systems. Shutting down the wrong engine led to a crash beside a motorway, though 79 of 126 survived. Adele also discussed irrational behavior under stress, citing the Manchester Airport disaster (1984), where 55 died from smoke inhalation during an evacuation. Post-crash recommendations, like strip lighting and wider exits, addressed irrational human behavior in emergencies, offering lessons for software in designing for stressed users.
Habituation and Complacency
Delta Airlines 1141 (1988) illustrated the risks of habituation, where routine dulls vigilance. Pilots, accustomed to the pre-flight checklist, failed to deploy flaps, missing a warning due to a modified takeoff alert system. The crash after takeoff killed 14. Adele likened this to software engineers ignoring frequent alerts, like her colleague Pete with muted notifications. She urged designing systems that account for human tendencies like habituation, ensuring alerts are meaningful and workflows prevent complacency. Her takeaways emphasized understanding users’ cognitive limits, balancing redundancy with simplicity, and prioritizing human-centric design to avoid software disasters.
Links:
[DevoxxPL2019] Functional Programming in Kotlin: Core Concepts and Applications
Lecturer
Venkat Subramaniam is an acclaimed software developer, author, and educator who founded Agile Developer, Inc., specializing in training and consulting on agile practices and programming languages. He holds a position as an instructional professor at the University of Houston, where he imparts knowledge on software engineering principles, and has authored several books on programming topics, including works on Kotlin and functional paradigms.
Abstract
This exploration delves into the principles of functional programming within the Kotlin language, contrasting it with imperative approaches and emphasizing declarative techniques, higher-order functions, lambda expressions, and lazy evaluation strategies. Through detailed examples, it examines how these elements streamline code, mitigate complexity, and support concurrent operations, while discussing methodological choices and their broader effects on software architecture.
Distinguishing Imperative and Declarative Paradigms: Establishing the Base
In software development, the choice of programming style profoundly influences the clarity and maintainability of code. Venkat initiates his discussion by highlighting the imperative style, where developers must specify not only the desired outcome but also the exact sequence of steps to achieve it. This method resembles providing exhaustive instructions, placing the onus on the programmer to manage every aspect of the process, which can introduce unnecessary intricacies that obscure the primary objective.
To illustrate, consider a scenario involving a collection of integers from one to ten, where the task is to calculate the sum of the doubles of all even numbers. In an imperative framework, one would typically declare a mutable variable to accumulate the result, then employ a loop to traverse the collection, apply a condition to identify even numbers, perform the doubling operation, and update the accumulator accordingly. Such an approach requires explicit handling of iteration and state changes, which can lead to errors if not managed meticulously. For instance, overlooking the initialization of the accumulator or mishandling the loop boundaries could yield incorrect results, thereby increasing the cognitive burden on the developer.
Conversely, the declarative style allows programmers to articulate solely what is needed, delegating the implementation details to underlying abstractions. This shift enables a focus on intent rather than mechanics, much like issuing a high-level command without detailing the execution path. Functional programming builds upon this by incorporating higher-order functions, which are capable of accepting other functions as arguments, generating new functions, or yielding functions as results. These constructs facilitate functional composition, where smaller, reusable units of behavior are combined to form more sophisticated operations without altering shared state.
Venkat underscores that while Kotlin permits imperative coding for familiarity, its support for declarative constructs encourages a move toward reduced complexity. By abstracting away low-level controls, developers can produce code that is more intuitive and less prone to defects. This transition has significant ramifications for large-scale systems, where maintaining code over time becomes paramount; declarative code tends to be more adaptable, facilitating easier modifications and extensions without widespread ripple effects.
Harnessing Lambda Expressions and Higher-Order Functions: Fundamental Tools
At the heart of Kotlin’s functional capabilities lie lambda expressions, which Venkat portrays as nameless functions designed to encapsulate behavior concisely and purely, meaning they avoid modifying external state or producing side effects. These expressions consist of a parameter list separated by an arrow from the body, enclosed in curly braces, with the return type inferred from the context to minimize verbosity.
The structure promotes brevity, ideally limiting the body to a single line to preserve readability. For example, incrementing each element in a list can be achieved with a lambda passed to the map function, transforming the collection in a one-to-one manner without explicit loops. However, when transformations yield multiple outputs per input—such as generating predecessors and successors for each number—standard mapping results in nested collections. To address this, flattening merges these into a single list, but performing mapping followed by flattening separately can be inefficient.
Venkat explains that flatMap elegantly combines these operations, applying the transformation and then consolidating the results. This is particularly useful for one-to-many mappings, ensuring the output remains a flat structure. Methodologically, selecting map for direct correspondences and flatMap for expansive transformations optimizes the pipeline, aligning with functional composition principles where functions chain to build complex logic from simple components.
Furthermore, higher-order functions extend this by treating functions as data, enabling dynamic behavior parameterization. The broader context is Kotlin’s hybrid nature, integrating object-oriented features with functional ones, allowing seamless interoperability. Analytically, this purity aids in reasoning about code; since functions depend only on inputs, outputs are predictable, simplifying testing and debugging. The consequences extend to concurrency, where absence of mutable state eliminates contention, making parallelization straightforward and safer in multi-threaded environments.
Implementing Lazy Evaluation: Optimizing Resource Utilization
A critical facet Venkat addresses is evaluation strategy, distinguishing eager from lazy approaches. Eager evaluation processes operations immediately, which can be wasteful for large datasets or when only partial results are needed. For instance, finding the double of the first even number greater than three in a list involves filtering for values exceeding three, then for evenness, doubling, and selecting the first—eagerly traversing the entire collection multiple times.
By converting the list to a sequence in Kotlin, operations become lazy, computing only as required. This defers execution until the terminal operation, such as retrieving the first element, halting further processing once the result is found. Venkat demonstrates this with print statements in filter and map functions, revealing that lazy sequences minimize calls, touching only necessary elements.
Methodologically, employing sequences for potentially infinite or voluminous data prevents unnecessary computations, akin to Java’s streams. However, developers must consciously opt for sequences, as list operations default to eagerness. The context here is performance-sensitive applications, where eager defaults could lead to inefficiencies. Implications include resource conservation in big data scenarios, enabling handling of streams that exceed memory capacity. Analytically, laziness embodies functional essence, allowing declarative chains without premature optimization concerns, thus promoting scalable designs in resource-constrained settings.
Broader Ramifications for Software Engineering: From Concurrency to Maintainability
Although functional programming bolsters concurrency by eschewing mutable state—thus avoiding locks and race conditions—Venkat posits that its chief merit lies in declarative reduction of accidental complexity, where code mirrors intent more closely. Imperative verbosity often embeds implementation details that hinder comprehension, whereas functional pipelines express logic fluidly.
In Kotlin, this manifests through native support for these idioms, blending with object-oriented paradigms for versatile architectures. Yet, judicious application is key; misusing eagerness or bloating lambdas undermines benefits. The consequences foster resilient systems, adaptable to change with minimal disruption. For practitioners, this encourages a mindset shift toward composition and purity, yielding codebases that are easier to evolve and collaborate on.
Ultimately, Kotlin’s functional features empower developers to craft elegant solutions, balancing expressiveness with efficiency, and paving the way for innovative software practices.
Links:
[DevoxxFR2014] Git-Deliver: Streamlining Deployment Beyond Java Ecosystems
Lecturer
Arnaud Bétrémieux is a passionate developer with 18 years of experience, including 8 professionally, specializing in open-source technologies, GNU/Linux, and languages like Java, PHP, and Lisp. He works at Key Consulting, providing development, hosting, consulting, and expertise services. Sylvain Veyrié, with nearly a decade in Java platforms, serves as Director of Delivery at Transparency Rights Management, focusing on big data, and has held roles in development, project management, and training at Key Consulting.
Abstract
This article investigates git-deliver, a deployment tool leveraging Git’s integrity guarantees for simple, traceable, and atomic deployments across diverse languages. It dissects the tool’s mechanics, from remote setup to rollback features, and discusses customization via scripts and presets, emphasizing its role in replacing ad-hoc scripts in dynamic language projects.
Core Principles and Setup
Git-deliver emerges as a Bash script extending Git with a “deliver” subcommand, aiming for simplicity, reliability, efficiency, and universality in deployments. Targeting non-Java environments like Node.js, PHP, or Rails, it addresses the pitfalls of custom scripts that introduce risks in traceability and atomicity.
A deployment target equates to a Git remote over SSH. For instance, creating remotes for test and production environments involves commands like git remote add test deliver@test.example.fr:/appli and git remote add prod deliver@example.fr:/appli. Deliveries invoke git deliver <remote> <version>, where version can be a branch, commit SHA, or tag.
On the target server, git-deliver initializes a bare Git repository alongside a “delivered” directory containing clones for each deployment. Each clone includes Git metadata and a working copy checked out to the specified version. Symbolic links, particularly “current,” point to the latest clone, ensuring a fixed path for applications and atomic switches— the link updates instantaneously, avoiding partial states.
Directory names incorporate timestamps and abbreviated SHAs, facilitating quick identification of deployed versions. This structure preserves history, enabling audits and rollbacks.
Information Retrieval and Rollback Mechanisms
To monitor deployments, git-deliver offers a “status” option. Without arguments, it surveys all remotes, reporting the current commit SHA, tag if applicable, deployment timestamp, and deployer. It also verifies integrity, alerting to uncommitted changes that might indicate manual tampering.
Specifying a remote yields a detailed history of all deliveries, including directory identifiers. Additionally, git-deliver auto-tags each deployment in the local repository, annotating with execution logs and optional messages. Pushing these tags to a central repository shares deployment history team-wide.
Rollback supports recovery: git deliver rollback <remote> reverts to the previous version by updating the “current” symlink to the prior clone. For specific versions, provide the directory name. This leverages preserved clones, ensuring exact restoration even if files were altered post-deployment.
Customization and Extensibility
Deployments divide into stages (e.g., init-remote for first-time setup, post-symlink for post-switch actions), allowing user-provided scripts executed at each. For normal deliveries, scripts might install dependencies or migrate databases; for rollbacks, they handle reversals like database adjustments.
To foster reusability, git-deliver introduces “presets”—collections of stage scripts for frameworks like Rails or Flask. Dependencies between presets (e.g., Rails depending on Ruby) enable modular composition. The “init” command copies preset scripts into a .deliver directory at the project root, customizable and versionable via Git.
This extensibility accommodates varied workflows, such as compiling sources on-server for compiled languages, though git-deliver primarily suits interpreted ones.
Broader Impact on Deployment Practices
By harnessing Git’s push mechanics and integrity checks, git-deliver minimizes errors from manual interventions, ensuring deployments are reproducible and auditable. Its atomic nature prevents service disruptions, crucial for production environments.
While not yet supporting distributed deployments natively, scripts can orchestrate multi-server coordination. Future enhancements might incorporate remote groups for parallel pushes.
In production at Key Consulting, git-deliver demonstrates maturity beyond prototyping, offering a lightweight alternative to complex tools, promoting standardized practices across projects.