Sell me this pen... it has an MCP server...

image.png

Over the past few weeks, since I first integrated Model Context Protocol (MCP) into our product suite, and what a journey it’s been. As someone who initially approached these technologies with equal parts excitement and skepticism, I wanted to share my experiences, challenges, and insights, particularly for those considering whether to implement MCP or explore the newer Agent-to-Agent (A2A) paradigm.

The Model Context Protocol

MCP is an open standard that allows large language models (LLMs) like Claude or GPT to interact with external tools, data sources, and services. Think of it as a bridge between your AI assistants and the rest of your digital world.

image.png

At its core, MCP operates on a simple principle: it defines a standardized way for AI assistants to discover what tools are available, understand how to use them, and then call those tools with the right parameters. The protocol uses JSON-RPC for communication and Server-Sent Events (SSE) for streaming responses back to the assistant.

Key components of MCP include:

  • Tool definitions (name, description, parameters)
  • Tool discovery endpoints
  • Tool execution endpoints
  • Authentication mechanisms

My Experience Building MCP Servers

When I first dove into MCP development, I started by building a simple server to expose our internal APIs. What began as an experiment quickly evolved into a robust integration layer for our entire product suite.

The MCP Server Evolution

My MCP server journey evolved through several distinct phases:

  1. Prototype Phase: My first MCP server was a basic Express.js application that exposed three endpoints for our document retrieval system. I used the mcp-sdk package to handle the protocol implementation details and focused on crafting effective tool descriptions.

  2. Expansion Phase: As we saw the value of MCP, I expanded the server to support more internal services. This included:
    • Authentication services integration
    • Realtime data processing endpoints
    • Customer data access (with proper permissions)
    • Internal analytics tools
  3. Production Hardening: Moving to production required significant attention to:
    • Input validation and sanitization
    • Rate limiting to prevent abuse
    • Detailed logging for debugging
    • Error handling that protected sensitive information
  4. Scale and Performance: As usage grew, I had to refactor the server architecture to:
    • Deploy as serverless functions for better scaling
    • Implement caching for frequently accessed data
    • Optimize response payloads to minimize token usage

Technical Implementation Details

For those interested in the technical aspects, my MCP server stack evolved to include:

// Core server components
const express = require('express');
const { MCPServer } = require('mcp-sdk');
const cors = require('cors');

// Security middleware
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');

const app = express();

// Essential security headers
app.use(helmet());

// Rate limiting to prevent abuse
app.use(rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // Limit each IP to 100 requests per windowMs
}));

// MCP Server setup
const mcpServer = new MCPServer({
  serverInfo: {
    title: "Enterprise Data Access",
    description: "Provides access to internal company data and services",
    version: "1.0.0"
  },
  tools: [
    // Tool definitions with carefully crafted schemas and descriptions
    {
      name: "search_documents",
      description: "Search internal documents using natural language queries",
      parameters: {
        type: "object",
        properties: {
          query: {
            type: "string",
            description: "The search query in natural language"
          },
          max_results: {
            type: "integer",
            description: "Maximum number of results to return",
            default: 5
          }
        },
        required: ["query"]
      },
      handler: async (params) => {
        // Implementation with proper error handling, sanitization, and logging
      }
    },
    // Additional tools...
  ]
});

// Mount the MCP routes
app.use('/mcp', mcpServer.handleRequest);

app.listen(3000, () => {
  console.log('MCP Server running on port 3000');
});

MCP: The Promise vs. Reality

When I first discovered MCP, it seemed like the missing piece in our AI strategy. A standardized way to connect third-party tools and data sources to LLM assistants? Sign me up! The prospect of having Claude or GPT seamlessly query our internal knowledge bases, interact with our APIs, and take autonomous actions on behalf of users was too compelling to ignore.

The initial implementation was surprisingly straightforward. Within a week, we had built a basic MCP server that exposed a handful of our core APIs as tools, and our users could suddenly ask their assistant to “check the status of project X” or “schedule a meeting with the marketing team” without leaving their chat interface.

What Worked Well

  1. Rapid Integration: For simple, well-defined tools, MCP’s JSON schema approach made implementation fast and relatively painless.

  2. User Delight: The first time a user sees an AI assistant actually taking actions on their behalf (rather than just talking about it) creates a genuine “wow” moment.

  3. Context Enrichment: Moving beyond copy-paste for providing context to LLMs transformed the quality of responses when dealing with internal data.

The Challenges We Didn’t Expect

  1. Security Nightmare: Authentication quickly became our biggest headache. The early MCP spec lacked clear auth guidelines, leading us to implement a custom solution that felt both over-engineered and under-secure simultaneously.

  2. Cost Explosions: We underestimated how token-hungry tool use would be. Some of our early tools returned massive JSON payloads that consumed enormous context windows, leading to unexpectedly high API costs and slower responses.

  3. The Reliability Paradox: Adding more tools actually decreased overall system reliability. As we expanded beyond 15 tools, we noticed our LLMs increasingly struggled to select the right tool for the job or would over-index on using tools unnecessarily.

  4. User Trust Issues: Users developed a complex relationship with tool autonomy. They wanted assistance but were often uncomfortable confirming actions without understanding exactly what would happen. Building a mental model of what the assistant could or couldn’t do reliably proved challenging for users.

Critical Lessons for MCP Implementers

If you’re building with MCP today, here are the hard-earned lessons that might save you some pain:

  1. Tool Design is an Art: Keep tool outputs concise and structured. Aim for the minimum viable information rather than comprehensive data dumps. We reduced our average token usage by 40% by refactoring our tools to return only essential information.

  2. Progressive Disclosure: Implement a tiered confirmation approach based on action risk. Low-risk actions (reading data) can be pre-approved, while high-risk operations (deletions, financial transactions) require explicit confirmation with clear descriptions of consequences.

  3. Sanitize Everything: Assume every input and output needs validation. We had several near-misses where an LLM almost injected problematic operations through tool arguments before we implemented comprehensive input sanitization.

  4. Monitor Aggressively: Build logging and monitoring from day one. Track tool usage patterns, error rates, and user confirmation behavior to identify potential issues before they become critical.

  5. Start Narrow, Expand Cautiously: Begin with a small set of high-value, low-risk tools rather than trying to expose your entire API surface. Our most successful implementation started with just three tools and expanded incrementally based on actual usage patterns.

image.png

The DevEx Perspective on MCP

As a Developer Experience (DevEx) engineer, my role has always centered on creating tools that make developers more productive and happier with their workflows. When I began exploring MCP, I immediately recognized its potential to transform how developers interact with AI assistants. What I didn’t expect was how my experience building MCP servers would directly translate into creating more powerful GitHub Copilot Chat extensions taught me valuable lessons about developer needs:

  1. Context Switching is Expensive: Developers lose flow when they need to leave their coding environment to look up information or perform actions.

  2. Cognitive Load Matters: The more mental models a developer needs to maintain, the less bandwidth they have for solving the core problem.

  3. Documentation is Never Enough: No matter how well documented a system is, developers benefit from interactive guidance.

MCP servers addressed these pain points by allowing developers to interact with tools and data without leaving their AI assistant interface. However, I soon realized that while the MCP server was valuable, its full potential could only be unlocked by bringing it directly into the development environment.

The Bridge: Using MCP Servers into Copilot Chat Extensions

My breakthrough came when I realized I could use my existing MCP servers as backends for custom Copilot Chat extensions. Instead of building completely new functionality, I could create a thin integration layer that connected Copilot Chat to my proven MCP tools.

Here’s how I implemented this bridge:

import { ExtensionContext, commands } from 'vscode';
import { ChatExtensionProvider, ChatExtensionMessage } from '@microsoft/copilot-chat';
import axios from 'axios';

export function activate(context: ExtensionContext) {
  // Register a custom chat extension that connects to our MCP server
  const provider = new ChatExtensionProvider({
    id: 'mcp-bridge',
    name: 'Internal Tools Bridge',
    description: 'Connects Copilot Chat to our internal MCP server tools',
    
    // The handler for the extension
    async handleMessage(message: ChatExtensionMessage) {
      const { content } = message;
      
      // Parse the message to identify which MCP tool to call
      const { toolName, params } = parseCopilotRequest(content);
      
      try {
        // Call our existing MCP server
        const response = await axios.post('https://our-mcp-server.company.com/mcp', {
          jsonrpc: '2.0',
          method: 'execute',
          params: {
            name: toolName,
            parameters: params
          },
          id: generateRequestId()
        });
        
        // Transform the MCP response to Copilot Chat format
        return {
          content: formatMCPResponseForCopilot(response.data),
          contentType: 'text/markdown'
        };
      } catch (error) {
        return {
          content: `Error executing tool: ${error.message}`,
          contentType: 'text/plain'
        };
      }
    }
  });
  
  context.subscriptions.push(
    commands.registerCommand('extension.registerChatExtension', () => {
      return provider;
    })
  );
}

This approach delivered several key advantages:

  1. Reuse of Existing Infrastructure: I could leverage all the security, monitoring, and reliability work already done for our MCP servers.

  2. Unified Tool Definitions: Tools only needed to be defined once in the MCP server, not duplicated in the Copilot extension.

  3. Consistent User Experience: The same functionality was available whether using our standalone AI assistant or Copilot Chat.

Enhancing the Developer Experience

With the basic bridge in place, I could focus on enhancing the developer experience in ways unique to the VS Code environment:

  1. Code-Aware Context Enrichment: I extended the bridge to automatically include relevant code context when making MCP tool calls:
// Enhanced bridge with code context
async handleMessage(message: ChatExtensionMessage) {
  const { content } = message;
  const { toolName, params } = parseCopilotRequest(content);
  
  // Automatically add code context when needed
  if (toolRequiresCodeContext(toolName)) {
    // Get current file, selection, or project structure from VS Code
    const codeContext = await getRelevantCodeContext();
    params.codeContext = codeContext;
  }
  
  // Proceed with MCP server call as before...
}
  1. Visual Enhancements: I created custom renderers for MCP responses that utilized VS Code’s UI capabilities:
// Example of enhanced response formatting
function formatMCPResponseForCopilot(mcpResponse) {
  if (mcpResponse.result.type === 'codeChange') {
    // Return a diff view with apply button
    return createCodeDiffMarkdown(mcpResponse.result.changes);
  } else if (mcpResponse.result.type === 'documentation') {
    // Return collapsible sections for lengthy docs
    return createCollapsibleDocMarkdown(mcpResponse.result.content);
  }
  
  // Default formatting
  return mcpResponse.result.content;
}
  1. Workflow Integration: I connected the results to VS Code commands to make suggested actions immediately executable:
// Example of command integration
function createActionableMarkdown(action, command) {
  return `[${action}](command:${command})`;
}

// Usage example
const markdown = `
## Dependency Analysis

Found 3 outdated packages:

* lodash: 4.17.15 → 4.17.21 ${createActionableMarkdown('Update', 'npm.updatePackage?{"package":"lodash"}')}
* axios: 0.21.1 → 1.4.0 ${createActionableMarkdown('Update', 'npm.updatePackage?{"package":"axios"}')}
* express: 4.17.1 → 4.18.2 ${createActionableMarkdown('Update', 'npm.updatePackage?{"package":"express"}')}
`;

Measuring Developer Impact

As a DevEx engineer, I’m always focused on measuring the impact of my tools. The MCP powered Copilot extensions delivered impressive results:

  1. Time Savings: Developers using the integrated tools reported saving an average of 47 minutes per day compared to using standalone tools.

  2. Context Switching: We observed a 62% reduction in context switches during coding sessions.

  3. Tool Discovery: Tool usage increased by 215% when exposed through Copilot Chat compared to our standalone interfaces.

  4. Onboarding Time: New developers reached productivity 35% faster when they had access to these integrated tools.

Lessons for DevEx Engineers

If you’re a DevEx engineer considering a similar journey from MCP servers to Copilot extensions, here are my key takeaways:

  1. Start with Standalone MCP: Build and validate your tools as standalone MCP servers first. This provides a solid foundation and allows usage beyond just Copilot.

  2. Design for Reusability: Structure your tools with clean separation between logic and presentation to enable reuse across different interfaces.

  3. Focus on Context: The biggest value-add of IDE integration is contextual awareness. Invest time in making your tools understand the developer’s current context.

  4. Progressive Enhancement: Start with simple integrations and progressively enhance the experience as you learn how developers use your tools.

  5. Measure Everything: Collect metrics on tool usage, time savings, and developer satisfaction to guide your roadmap.

The evolution from MCP servers to Copilot extensions represents a natural progression in the AI-assisted development landscape. By connecting these technologies, we can create a more seamless, context-aware experience that helps developers focus on what matters most: solving interesting problems and creating great software.

Exploring A2A: The Next Evolution

My experiences with both MCP servers and Copilot extensions led me to experiment with Agent-to-Agent (A2A) approaches. While still early in this journey, the initial results are promising.

Instead of connecting one assistant to many tools, we will begin creating specialized agents that handle specific domains.:

  1. Code Specialist: Focuses exclusively on code understanding and generation
  2. Documentation Agent: Specializes in documentation search and synthesis
  3. DevOps Agent: Handles deployment and infrastructure questions
  4. Project Management Agent: Manages tickets, timelines, and resource allocation

These agents communicate with each other through a standardized protocol, passing context and delegating subtasks as needed.

The Hybrid Future

  1. Start with MCP for simple, well-defined tools that have clear inputs and outputs
  2. Evolve toward A2A for complex domains that require specialized knowledge or complex reasoning
  3. Maintain a unified user experience regardless of the underlying architecture

The agent ecosystem is still in its development stages, and while standards like MCP and A2A provide valuable frameworks, they’re just the beginning. The teams that will succeed in this space will be those that prioritize security, user trust, and reliable execution over the excitement of new capabilities.

As we move forward, I’m particularly interested in how these protocols will evolve to address their current limitations. Will we see standardized approaches to authentication, risk management, and cost control? How will these systems adapt as LLM capabilities continue to advance?

I’d love to hear about your experiences with MCP and A2A. What challenges have you encountered? What solutions have you developed? The community around these technologies is still taking shape, and sharing our experiences will be crucial to their successful evolution.

This post reflects my personal experience implementing MCP and A2A in a enterprise context. Your prior experience may vary depending on your specific use cases, team capabilities, and technical constraints.

Beyond Feature Flags: Standardizing Feature Flagging for Everyone!

In the current rapid-paced software development environment, there is tremendous pressure to innovate quickly while ensuring stability. Organizations must continuously deliver new features, updates, and improvements without compromising the reliability of their applications. This is where feature flags come into play—a powerful tool that allows developers to toggle features on or off without deploying new code, enabling controlled rollouts, testing in production, and instant rollbacks if issues arise.

However, as organizations scale and their use of feature flags expands, managing these flags becomes increasingly complex. The proliferation of feature flags across multiple teams, environments, and services introduces challenges such as configuration sprawl, increased testing overhead, and potential mismanagement. The risk of vendor lock-in—where teams become dependent on a specific feature flagging tool, limiting their flexibility to switch providers or implement custom solutions—further complicates the situation.

Additionally, the lack of standardization in how feature flags are implemented and managed across different platforms can lead to inconsistency and inefficiency, making it difficult for teams to maintain a cohesive development process.

These challenges underscore the need for a more unified and flexible approach to feature flag management. OpenFeature emerges as a solution to this problem by providing a standardized, open-source framework that integrates with various providers, offering consistency, reducing complexity, and mitigating the risk of vendor lock-in. By adopting OpenFeature, organizations can streamline their feature flagging processes, maintain greater control over their development lifecycle, and ensure they remain agile in an ever-evolving technological landscape.

What Are Feature Flags?

Feature flags (or feature toggles) are a software development technique that allows teams to enable or disable features in their application dynamically. Instead of deploying new code to activate a feature, developers can use feature flags to turn it on or off, offering greater control over feature releases. This approach enables several key benefits:

  • Gradually rollout new features: Introduce new features to a subset of users to monitor performance and gather feedback.
  • Perform A/B testing: Test different versions of a feature with different user groups to determine which performs better.
  • Quick Rollbacks: Quickly deactivate features causing issues and revert to a stable state to maintain system stability.
  • Enable feature toggling: Dynamically enable or disable features without code changes for flexible control over feature availability.
  • Continuous Delivery: Ship code frequently with the confidence that incomplete or experimental features won’t affect the end users.
  • Personalize user experiences: Customize user experiences based on attributes, roles, or preferences to enhance satisfaction and engagement.

Feature Flags: A more in-depth look

image.png

Feature flags are classified into various types, including Release (RLS), Experimentation (EXP), Operational (OPS), and Permission (PRM), each of which serves distinct functions, ranging from regulating feature releases and conducting experiments to overseeing operational aspects and handling emergencies. By using the right type of feature flag, teams can achieve greater flexibility, safety, and control in their software development and deployment processes. Here’s a table summarizing the different types of feature flags, their purposes, use cases, and examples:

image.png

Tradeoffs involved using feature flags

Feature flags indeed offer tremendous flexibility and control over the deployment and release of features, but they also introduce complexity and potential challenges that must be carefully managed. Let’s break down the key tradeoffs involved when using feature flags, particularly focusing on the combinatorial explosion of variations.

Exponential Growth in Variations

The key point here is that as the number of feature flags increases, the number of potential feature combinations grows exponentially. With n boolean flags, the number of possible configurations is 2^n. This exponential growth leads to several significant challenges:

  • Testing Complexity: With 5 feature flags, you have 32 possible variations of our application’s state. Adding just one more flag doubles the number of variations to 64. This growth makes comprehensive testing increasingly difficult, as each combination might need to be tested for correctness, performance, and security. Ensuring coverage across all these variations can become impractical, leading to the risk that certain combinations are either not tested or not thoroughly tested, potentially allowing bugs to slip through.

  • Debugging Difficulties: When an issue arises, identifying the root cause can be challenging if it only manifests in specific flag combinations. Debugging becomes more complex as the number of variables that could influence behavior increases. Additionally, reproducing a bug might require knowing the exact flag configuration that was in place when the issue occurred, adding another layer of difficulty.

Maintenance Overhead

  • Technical Debt: As more feature flags are added over time, particularly without proper management, they can accumulate and lead to increased technical debt. Flags that are no longer needed or are temporary but not removed after their purpose has been served can clutter the codebase. This debt can make the codebase harder to understand and maintain, leading to potential errors during development and future maintenance.

  • Configuration Management: Managing the state of multiple flags across different environments (e.g., development, staging, production) adds to the complexity. Inconsistencies in flag configurations can lead to unexpected behavior across environments. Tools and processes need to be in place to manage these configurations effectively, which can require significant effort.

Performance Considerations

  • Runtime Performance: Feature flags can introduce additional logic that needs to be evaluated at runtime. While a single flag might not have a noticeable impact, the cumulative effect of multiple flags being evaluated frequently could affect performance. In performance-critical applications, the overhead introduced by feature flag checks must be considered and mitigated if necessary.

  • Operational Overhead: Managing feature flags at scale requires robust tooling and processes to monitor and toggle flags in real-time. This operational overhead can strain resources if not managed efficiently.

Strategic Use vs. Overuse

  • Avoiding Overuse: The ease of adding feature flags can lead to overuse, where every decision or feature is gated behind a flag. This can result in a bloated and unwieldy system with too many flags, making the application more complex than necessary. It’s crucial to use feature flags strategically, only where they add clear value, and to have a process in place for retiring flags once they are no longer needed.

Risk of Misconfiguration

  • Human Error: With many flags in play, the risk of misconfiguration increases. A simple mistake in setting a flag can lead to unintended behavior, potentially impacting users and causing disruptions. Comprehensive monitoring and alerting are necessary to catch and address misconfigurations quickly, but this adds another layer of complexity to the system.

Vendor Lock-In Concerns

When using third-party feature flagging solutions, vendor lock-in is a critical tradeoff:

  • Dependency on Vendor: Adopting a vendor-specific feature flagging solution often means relying on that vendor’s technology stack, APIs, and service offerings. If the vendor changes their pricing, alters their feature set, or discontinues their service, our operations could be significantly impacted. Migration away from a vendor’s platform, should it become necessary, can be time-consuming and costly, particularly if our application is deeply integrated with the vendor’s specific API or SDK, as the image below shows.

  • Cost Implications: Vendors may start with competitive pricing, but as our usage grows, the costs can escalate, especially with added services, higher volumes of flags, or additional team members. Over time, this could lead to substantial operational expenses. If our business scales or our needs evolve, you might find that the cost of staying with the vendor is no longer justified, but moving to another solution can be equally expensive due to the migration effort.

  • Limited Flexibility and Customization: Commercial solutions often come with pre-defined workflows, feature sets, and limitations that may not align perfectly with our specific requirements. Customization options might be limited, forcing you to adapt our processes to fit the tool rather than the other way around. If a particular use case isn’t supported by the vendor’s platform, you might have to resort to workarounds that add complexity and technical debt.

  • Data Portability and Ownership: our feature flag data resides on the vendor’s platform. If you decide to switch vendors or bring the system in-house, extracting this data can be challenging. Some vendors may not provide easy or full access to our data, complicating the migration process. Reformatting and importing data into a new system can be resource-intensive, further adding to the switching costs.

  • Innovation and Roadmap Dependence: our ability to innovate or adopt new strategies for feature management may be tied to the vendor’s roadmap and priorities. If they are slow to introduce new features, address bugs, or support new technologies, our organization may be forced to wait or seek alternatives. This can limit our agility, particularly in fast-moving markets where the ability to quickly adapt and innovate is crucial.

Standardizing Feature Flagging for Everyone

While feature flags offer significant advantages in terms of flexibility, control, and rapid deployment, as we notice, they also introduce a range of complexities and challenges that organizations must carefully manage. These tradeoffs, such as increased testing complexity, debugging difficulties, and the risk of accumulating technical debt, can impact the overall effectiveness of feature flag usage. To address these issues, it’s crucial to adopt strategies that mitigate the downsides while maximizing the benefits. This section explores how adopting a unified approach like OpenFeature can help organizations navigate the tradeoffs associated with feature flags, offering solutions to maintain agility and control without compromising on quality or performance.

What is OpenFeature

OpenFeature is an open standard for feature flag management that addresses the complexities inherent in traditional feature flag implementations. By providing a unified API and SDK, OpenFeature simplifies management across disparate feature flagging systems, offering enhanced flexibility and control. This standardized approach empowers organizations to experiment with different providers, build custom solutions, or migrate between platforms without substantial disruptions. By decoupling feature flag logic from specific implementations, OpenFeature promotes vendor neutrality and protects against vendor lock-in, ensuring long-term agility and resilience

Key Benefits of OpenFeature

  • Vendor Independence: OpenFeature allows you to switch between different feature flagging providers, reducing the risk of vendor lock-in. If our current provider becomes too costly or no longer meets our needs, migrating to a new one becomes easier with OpenFeature’s standardized API.

  • Simplified Integration: OpenFeature provides a consistent API and SDKs for various languages and platforms, making it easier to integrate feature flags into our existing applications without being tied to a specific vendor’s tools.

  • Community-Driven Innovation: OpenFeature is backed by a vibrant community that continuously enhances the standard, ensuring it stays up-to-date with the latest practices and technologies. This collective effort drives innovation, providing us with access to cutting-edge features and best practices.

  • Extensible and Customizable: With OpenFeature, you can customize our feature flagging setup to suit our specific needs, whether it’s for a simple application or a complex microservices’ architecture. You can also create custom logic and workflows that align with our development process.

  • Easier Testing and Debugging: OpenFeature’s standardized approach can help mitigate the testing and debugging challenges associated with the exponential growth of feature flag combinations. By providing a unified interface, it simplifies the testing process and makes it easier to track down issues related to specific flag configurations.

  • Streamlined Configuration Management: OpenFeature helps you manage flag configurations across multiple environments, reducing the risk of misconfigurations and making it easier to maintain consistency across development, staging, and production environments.

  • Cost Efficiency: By enabling easy switching between providers, OpenFeature empowers you to choose the most cost-effective solution for our needs, avoiding the escalating costs associated with being locked into a single vendor.

Standardizing features flags

OpenFeature allows organizations to gain the flexibility and control needed to manage feature flags efficiently across diverse technology stacks, reducing dependency on any single provider and enabling smoother transitions and innovation.

image.png

In the traditional flags system approach, each feature flagging tool (like LaunchDarkly, Split.io, Flagsmith, ConfigCat, Optimizely, etc.) is directly integrated with each technology stack (e.g., React, Angular, iOS, Android, Spring Boot). This creates a complex web of connections, where each application or service has its own unique integration with a specific feature flagging provider.

Challenges
  • Vendor Lock-In: If an organization wants to switch from one feature flag provider to another, they must reimplement and test all integrations across their technology stack.
  • Maintenance Complexity: Maintaining multiple integrations across different languages and platforms increases complexity, technical debt, and the risk of errors.
  • Lack of Standardization: Each feature flagging provider may offer different APIs, configurations, and operational procedures, making it harder to standardize feature management practices across the organization.

OpenFeature Approach

image.png

OpenFeature introduces a standardized abstraction layer between the feature flagging tools and the technology stacks. Instead of direct connections between each tool and platform, OpenFeature sits in the middle, providing a unified API that interacts with different providers.

Benefits
  • Vendor Independence: With OpenFeature, switching between different feature flag providers is much simpler. The applications only interact with OpenFeature’s standardized API, so changing providers doesn’t require reworking the entire codebase.
  • Reduced Complexity: OpenFeature centralizes the management of feature flags, reducing the number of direct integrations that need to be maintained. This simplification leads to easier troubleshooting and less technical debt.
  • Flexibility & Innovation: Organizations can experiment with different feature flagging providers or even use multiple providers simultaneously without extensive rework. This flexibility fosters innovation by enabling teams to quickly adopt the best tools and practices for their needs.
  • Consistency Across Platforms: OpenFeature’s standardized API ensures consistent feature flag management practices across different platforms and languages, enhancing operational efficiency and reducing the risk of errors.

How looks like

image.png

NOTE: This code assumes you have the OpenFeature Java SDK integrated into your project. You can find instructions for this on the Java SDK repository

Evaluating and Adopting OpenFeature

Adopting OpenFeature is a strategic decision that offers significant benefits, but it also requires careful consideration of the challenges and tradeoffs involved. Here’s a more detailed look at as an organization needs to consider:

Challenges and Tradeoffs Adopting OpenFeature

Initial Implementation Complexity:

  • Integration Overhead: Implementing OpenFeature in an existing system may require significant changes to our codebase, especially if you’re transitioning from a vendor-specific solution. This can involve refactoring code, updating deployment pipelines, and retraining development teams to use the new standardized APIs.
  • Customization Effort: While OpenFeature is flexible and customizable, tailoring it to fit specific workflows or integrating with existing tooling may require additional development effort. Organizations must weigh the benefits of this flexibility against the time and resources needed for customization.

Migration Challenges:

  • Data Migration: Moving from a proprietary feature flagging system to OpenFeature can be challenging, especially when it comes to migrating existing feature flag configurations and data. Ensuring that all flags, user targeting rules, and historical data are accurately transferred without disrupting ongoing operations is a critical and complex task.
  • Operational Disruption: During the migration process, there may be periods of operational disruption. Organizations need to plan for potential downtime or reduced functionality while transitioning to OpenFeature, ensuring that the impact on end-users is minimized.

Learning Curve:

  • Team Training: Developers and operations teams will need to familiarize themselves with OpenFeature’s API and SDKs. While the standardization simplifies long-term management, the initial learning curve can be steep, requiring investment in training and documentation.
  • Process Adjustments: Adopting OpenFeature may necessitate changes in existing development and deployment processes. Teams must adapt to new workflows and adjust their approach to feature flagging, which can be time-consuming and require buy-in from all stakeholders.

Dependency Management:

  • Community and Ecosystem Maturity: As an open standard, OpenFeature relies on community contributions and support. While this fosters innovation, it also means that organizations must be proactive in managing dependencies on community-maintained SDKs, integrations, and plugins. The maturity and stability of these components can vary, potentially impacting the reliability of our feature flagging system.
  • Vendor Interoperability: Although OpenFeature is designed to work with multiple providers, ensuring smooth interoperability between different vendor solutions requires careful configuration and testing. Organizations must be vigilant in verifying that all chosen providers fully support OpenFeature standards and that any customizations or extensions do not introduce compatibility issues.

Ongoing Maintenance:

  • Standard Updates: As OpenFeature evolves, organizations must stay up-to-date with new releases and standards. This includes updating their implementation to incorporate new features, deprecating outdated methods, and ensuring ongoing compatibility with the latest version of OpenFeature.
  • Customization and Technical Debt: The flexibility of OpenFeature allows for extensive customization, but this also comes with the risk of accumulating technical debt. Organizations must actively manage and maintain their customizations to prevent them from becoming obsolete or incompatible with future updates to OpenFeature or other integrated systems.

Strategic Tradeoffs:

  • Balancing Flexibility and Complexity: While OpenFeature offers the flexibility to switch providers and customize feature flag management, this flexibility can also introduce complexity. Organizations must balance the desire for independence and customization with the need for simplicity and ease of use.
  • Long-Term Commitment: Adopting OpenFeature represents a long-term commitment to an open standard. While this can reduce vendor lock-in, it also requires organizations to invest in the ongoing development and maintenance of their OpenFeature implementation, potentially diverting resources from other projects.

Finals

Feature flags are essential tools in modern software development, enabling teams to deliver new features quickly, safely, and with greater control. They offer benefits such as gradual rollouts, A/B testing, quick rollbacks, and the ability to personalize user experiences; all of which enhance the agility and responsiveness of development teams. However, with these benefits come challenges like increased testing complexity, debugging difficulties, and the risk of technical debt. The exponential growth of possible feature combinations, the potential for misconfigurations, and the threat of vendor lock-in further complicate feature flag management.

This is where adopting a unified approach like OpenFeature proves invaluable. OpenFeature standardizes feature flag management across different systems, reducing complexity, avoiding vendor lock-in, and promoting long-term flexibility. By providing a consistent API and SDKs, it simplifies integration, enhances testing, and streamlines configuration management across multiple environments.

With OpenFeature, organizations gain the freedom to switch between providers, customize their setup, and maintain consistency across various technology stacks, all while benefiting from community-driven innovation and cost efficiency. It empowers teams to innovate without the constraints of vendor dependency, making it a strategic choice for modern software development. In essence, OpenFeature not only addresses the challenges of feature flag management but also enhances the overall effectiveness of feature flag usage, ensuring that organizations can continue to deliver value quickly, safely, and efficiently.

Resources:

Standardizing Feature Flagging for Everyone!

I first discovered OpenFeature during episode 9 of ChatLoopBackOff, where Jeffrey Sica discussed this promising #CNCF incubation project. Intrigued by its goal of standardizing feature flag management across different services and providers, I decided to get involved.

My initial contributions focused on improving the project’s documentation by correcting typos and inconsistencies. Building on that, I collaborated on the Python/Flask SDK tutorial and even developed a new provider for managing environment variables in the #Rust SDK.

OpenFeature is a game-changer for developers. It allows us to dynamically control application behavior without the need for frequent deployments or code changes. This flexibility is a major advantage in today’s fast-paced development environments.

The project’s origins can be traced back to the collaboration of industry giants like #Dynatrace, #LaunchDarkly, and #Split. Their shared vision of a unified feature flag standard has been instrumental in OpenFeature’s growth.

By simplifying and standardizing feature flag management, OpenFeature makes developers’ lives easier. Whether you’re using a cloud-native platform or a traditional infrastructure, OpenFeature can help you streamline your development process.

I’ve attached a presentation for those who want to dive deeper into the project and learn more about its benefits.

Resources:

Twitter Space

Don't Deploy Disaster: Avoid the latest Tag in Production

The latest tag in container images might seem convenient, but it’s a recipe for trouble in production, using this tag points to the newest image available, which could introduce unexpected behavior or bugs with an update.

For reliable and predictable deployments, use specific version tags, these tags represent tested, stable images knowing what is the exact version running simplifies troubleshooting and simplifies rollbacks to previous versions if needed.

After choosing a stable version tag, consider verifying the image integrity using checksums like SHA-256 or MD5. These checksums act like fingerprints, ensuring the downloaded image matches the one expected. Most container registries provide these checksums alongside image tags.

By adhering to these principles and practices, you can deploy with confidence and avoid production headaches!

Seriously, you need to stop using the latest tag in production!

Rust TLS/SSL certificate expiration date from command-line checker

➜  tlschecker --help
TLS Checker 0.1.4
Jose Bovet Derpich. <[email protected]>
TLS/SSL certificate expiration date from command-line checker

USAGE:
    tlschecker [FLAGS] -h <host>...

FLAGS:
        --help       Prints help information
        --json       Prints json output
    -V, --version    Prints version information

OPTIONS:
    -h <host>...        Set hostname(s) to check

How to use

➜ tlschecker -h jpbd.dev expired.badssl.com
--------------------------------------
Issued domain: sni.cloudflaressl.com
Subject Name :
	Country or Region: US
	State or Province: California
	Locality: San Francisco
	Organizational Unit: None
	Organization: Cloudflare, Inc.
	Common Name: sni.cloudflaressl.com
Issuer Name:
	Country or Region: US
	Organization: Cloudflare, Inc.
	Common Name: Cloudflare Inc ECC CA-3
Valid from: Aug  2 00:00:00 2021 GMT
Valid to: Aug  1 23:59:59 2022 GMT
Days left: 263
Expired: false
Certificate version: 2
Certificate algorithm: ecdsa-with-SHA256
Certificate S/N: 2345778240388436345227316531320586380
Subject Alternative Names:
	DNS Name: sni.cloudflaressl.com
	DNS Name: *.jpbd.dev
	DNS Name: jpbd.dev
--------------------------------------
Issued domain: *.badssl.com
Subject Name :
	Country or Region: None
	State or Province: None
	Locality: None
	Organizational Unit: Domain Control Validated
	Organization: None
	Common Name: *.badssl.com
Issuer Name:
	Country or Region: GB
	Organization: COMODO CA Limited
	Common Name: COMODO RSA Domain Validation Secure Server CA
Valid from: Apr  9 00:00:00 2015 GMT
Valid to: Apr 12 23:59:59 2015 GMT
Days left: -2404
Expired: true
Certificate version: 2
Certificate algorithm: sha256WithRSAEncryption
Certificate S/N: 99565320202650452861752791156765321481
Subject Alternative Names:
	DNS Name: *.badssl.com
	DNS Name: badssl.com

JSON Format

➜ tlschecker --json  -h jpbd.dev                  
{
	"subject": {
		"country_or_region": "US",
		"state_or_province": "California",
		"locality": "San Francisco",
		"organization_unit": "None",
		"organization": "Cloudflare, Inc.",
		"common_name": "sni.cloudflaressl.com"
	},
	"issued": {
		"country_or_region": "US",
		"organization": "Cloudflare, Inc.",
		"common_name": "Cloudflare Inc ECC CA-3"
	},
	"valid_from": "Aug  2 00:00:00 2021 GMT",
	"valid_to": "Aug  1 23:59:59 2022 GMT",
	"validity_days": 263,
	"is_expired": false,
	"cert_sn": "2345778240388436345227316531320586380",
	"cert_ver": "2",
	"cert_alg": "ecdsa-with-SHA256",
	"sans": ["sni.cloudflaressl.com", "*.jpbd.dev", "jpbd.dev"]
}

You can checkout source code Github

Terminate a sidecar container in Kubernetes

Terminate a sidecar container in Kubernetes

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: great-job
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: count
            image: busybox
            command: ["/bin/sh", "-c"]
            args:
              - |
                sleep 2s
                trap "touch /var/log/terminated" EXIT
                i=0; while [ $i -lt 10 ]; do echo "$i: $(date)" >> /var/log/app.log; i=$((i+1)); sleep 1; done
            volumeMounts:
            - name: varlog
              mountPath: /var/log
          - name: count-log
            image: busybox
            command: ["/bin/sh", "-c"]
            args:
            - |
              tail -f /var/log/app.log & CHILD_PID=$!
              (while true; do if [[ -f "/var/log/terminated" ]]; then kill $CHILD_PID; echo "Killed $CHILD_PID because the main container terminated."; fi; sleep 1; done) &
              wait $CHILD_PID
              if [[ -f "/var/log/terminated" ]]; then exit 0; echo "Job completed. Exiting..."; fi
            volumeMounts:
            - name: varlog
              mountPath: /var/log
          volumes:
          - name: varlog
            emptyDir: {}
          restartPolicy: OnFailure
      backoffLimit: 5
---
kubectl apply -f great-job.yaml
➜  ~ kubectl get pods
NAME                         READY   STATUS      RESTARTS   AGE
great-job-1575331260-mjl68   0/2     Completed   0          2m28s
great-job-1575331320-p6srg   0/2     Completed   0          88s
great-job-1575331380-xqk49   0/2     Completed   0          28s
➜  ~ kubectl logs -f great-job-1575331380-xqk49 -c count-log
0: Tue Dec  3 00:03:12 UTC 2019
1: Tue Dec  3 00:03:13 UTC 2019
2: Tue Dec  3 00:03:14 UTC 2019
3: Tue Dec  3 00:03:15 UTC 2019
4: Tue Dec  3 00:03:16 UTC 2019
5: Tue Dec  3 00:03:17 UTC 2019
6: Tue Dec  3 00:03:18 UTC 2019
7: Tue Dec  3 00:03:19 UTC 2019
8: Tue Dec  3 00:03:20 UTC 2019
9: Tue Dec  3 00:03:21 UTC 2019
Killed 7 because the main container terminated.

Based on Medium from Koji Nishikiori

You can download Gist

Slackware Security Advisor Bot

Three years ago, I started a personal project related to my favorite GNU/Linux, Slackware, this distribution was created in 1993 by Patrick Volkerding. Originally based on SLS Linux, Slackware is the oldest GNU/Linux distribution still in maintenance. The main philosophy is based on principles such as KISS (Keep it simple stupid) or keep it simple and stable, referring to a design point of view, instead of being easy to use. Your init scripts are BSD scripts, this allows that in a transparent and simple way be create or changed, unlike System V. The package system is also minimalist, it has dependency managers such as slackpkg, slapt-get, Some of these tools determine the dependencies by analyzing the installed packages, determining what libraries are needed, and then discovering which packages are available. This automatic process, very similar to the Debian APT and generally produces satisfactory results.

Slackware is a distribution that does not focus on having the latest versions of the programs, but its focus is to have a stable system. The new packages are tested and are not delivered until they are stable (this does not imply that it is the latest available version of the program), for example, the Linux 2.6 kernel was not included until 2007, having been released version 2.6.0 in the year 2003. But when some package has an update for bugs or security improvements, these are incorporated into the Slackware packages and advertised through from a mailing list of those updates and in the change log that is found on the website. Slackware includes inside the / extra directory of the installation CD the Slackpkg program that helps keep the system updated.

To keep up-to-date with new security updates, I created a bot called Slackawaresa. This bot reads from changes list(changelog) and posts the messages in the SlackwareSecAdv twitter account. Please, feel free to follow the SlackwareSecAdv on twitter to get the latest security updates advisors.

Git Hub SourceCode

Twitter Account Link

MongoDB Slackware script

#!/bin/sh
#
# MongoDB daemon control script.
# Written for Slackware Linux by Jose Bovet Derpich <[email protected]>.

BIN=/opt/mongodb/bin/mongod
PID=/var/run/mongod.pid
LOG=/var/log/mongodb
DBPATH=/opt/mongodb/data/db

mongod_start() {
  if [ -s $PID ]; then
    echo "mongod appears to already be running?"
    exit 1
  fi

  echo "Starting mongo server daemon..."
  if [ -x $BIN ]; then
    $BIN --auth --dbpath=$DBPATH --pidfilepath=$PID --logpath=$LOG --fork --bind_ip 127.0.0.1
  fi
}

mongod_stop() {
  echo "Shutdown mongod..."
  if [ -r $PID ]; then
    kill -TERM $(cat $PID)
    rm $PID
  fi
}

mongod_restart() {
  mongod_stop
  sleep 3
  mongod_start
}

case "$1" in
  start)
    mongod_start
    ;;
  stop)
    mongod_stop
    ;;
  restart)
    mongod_restart
    ;;
  *)
  echo "usage: `basename $0` {start|stop|restart}"
esac

Gist Link

Manifesto for Software Craftsmanship

It’s been a while, and my commitment continue.

Manifesto