<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="/feeds/atom-style.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://feyzan.netlify.app</id>
    <title>Fauzan Fathurrahman</title>
    <updated>2026-05-17T09:21:47.806Z</updated>
    <generator>Astro Chiri Feed Generator</generator>
    <author>
        <name>Fauzan Fathurrahman</name>
        <uri>https://feyzan.netlify.app</uri>
    </author>
    <link rel="alternate" href="https://feyzan.netlify.app"/>
    <link rel="self" href="https://feyzan.netlify.app/atom.xml"/>
    <subtitle>Full-stack engineer &amp; technical leader. Building scalable products, automating pipelines, and creating impact through code.</subtitle>
    <rights>Copyright © 2026 Fauzan Fathurrahman</rights>
    <entry>
        <title type="html"><![CDATA[From Hours to Seconds: Automating Test Execution Tickets]]></title>
        <id>https://feyzan.netlify.app/ai-test-execution-automation</id>
        <link href="https://feyzan.netlify.app/ai-test-execution-automation"/>
        <updated>2025-11-02T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p><img src="https://feyzan.netlify.app/.netlify/images?url=_astro%2Ftx-gen.D5Q1HPp1.png&amp;fm=webp&amp;w=800&amp;h=641" alt="Test Execution Automation" /></p>
<h2>The Challenge</h2>
<p>Once test cases are created for a story, QA teams need to create test execution tickets to track which tests have been run and their results. Manually creating these tickets and including all test case details is tedious and error-prone.</p>
<p>Without automation, this becomes a bottleneck that slows down the testing workflow and creates inconsistency.</p>
<h2>The Solution</h2>
<p>I built a <mark>Python-based GitHub Action</mark> that <mark>automatically creates test execution tickets</mark> in Jira. The tool fetches all linked test cases from a story, creates an execution ticket, and includes all test case details—ready for QA execution tracking.</p>
<h2>How It Works</h2>
<h3>1. Initialize Clients</h3>
<p>When triggered, the automation:</p>
<ul>
<li>Establishes connection to Jira API</li>
<li>Validates credentials and permissions</li>
<li>Prepares for ticket creation</li>
</ul>
<h3>2. Create Test Execution</h3>
<p>The system creates test execution tickets with:</p>
<ul>
<li><mark>All linked test cases included</mark> from the story</li>
<li>Test case details and steps embedded</li>
<li>Standardized naming conventions</li>
<li>Pre-configured fields and metadata</li>
<li>Optional version tracking</li>
</ul>
<h3>3. Link to Stories</h3>
<p>Generated test execution tickets are automatically:</p>
<ul>
<li>Linked to the original story tickets</li>
<li>Tagged with execution metadata</li>
<li>Ready for QA team assignment</li>
<li>Tracked in test dashboards</li>
</ul>
<h2>The 3-Step Process</h2>
<pre><code>Story IDs
       ↓
[Trigger Automation]
       ↓
Initialize Jira Clients → Fetch Linked Test Cases → Prepare Test Data
       ↓
Create Test Execution Tickets (with all test cases included)
       ↓
Populate Test Steps → Link to Stories → Ready for Execution
       ↓
Test Execution Tickets Ready with All Test Cases
</code></pre>
<h2>Key Features</h2>
<h3>Automated Ticket Creation</h3>
<ul>
<li><mark>No manual ticket creation</mark></li>
<li><mark>All test cases automatically included</mark></li>
<li>Consistent naming and structure across all tickets</li>
<li>Pre-populated with story context and test details</li>
<li>Standardized fields and metadata</li>
</ul>
<h3>Smart Linking</h3>
<ul>
<li>Automatically links to parent story tickets</li>
<li>Maintains traceability throughout workflow</li>
<li>Supports version tracking</li>
<li>Creates audit trail</li>
</ul>
<h2>Technical Architecture</h2>
<table>
<thead>
<tr>
<th>Component</th>
<th>Details</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Language</strong></td>
<td>Python</td>
</tr>
<tr>
<td><strong>Platform</strong></td>
<td>GitHub Actions</td>
</tr>
<tr>
<td><strong>Input</strong></td>
<td>Story IDs</td>
</tr>
<tr>
<td><strong>Processing</strong></td>
<td>Jira API client initialization</td>
</tr>
<tr>
<td><strong>Output</strong></td>
<td>Linked test execution tickets</td>
</tr>
</tbody>
</table>
<h2>Real-World Impact</h2>
<ul>
<li><mark><strong>Faster Test Planning</strong></mark>: Test execution tickets with all test cases created in seconds</li>
<li><mark><strong>Complete Coverage</strong></mark>: All test cases automatically included in execution tickets</li>
<li><mark><strong>Consistency</strong></mark>: Every test execution ticket follows the same structure</li>
<li><mark><strong>Traceability</strong></mark>: Clear links between stories, test cases, and executions</li>
<li><mark><strong>Scalability</strong></mark>: Works for any number of stories and test cases simultaneously</li>
</ul>
<h2>Benefits for QA Teams</h2>
<ol>
<li><mark><strong>Reduced Manual Work</strong></mark>: Eliminate repetitive ticket creation tasks</li>
<li><mark><strong>Better Organization</strong></mark>: Standardized structure makes tracking easier</li>
<li><mark><strong>Faster Onboarding</strong></mark>: New QA members get consistent ticket structure</li>
</ol>
<h2>Use Cases</h2>
<ul>
<li><strong>Sprint Planning</strong>: Create test execution tickets with all test cases for sprint stories</li>
<li><strong>Release Management</strong>: Track test execution across versions with version parameter</li>
<li><strong>Bulk Testing</strong>: Handle multiple stories and their test cases without manual work</li>
<li><strong>Test Dashboards</strong>: Maintain clear hierarchy with test cases embedded in executions</li>
<li><strong>Test Tracking</strong>: Execute and track results for all test cases in one ticket</li>
</ul>
<h2>Workflow Integration</h2>
<p>This automation fits perfectly into your Jira workflow:</p>
<ol>
<li>Stories are created with acceptance criteria</li>
<li>Test cases are created and linked to stories (via AI Test Case Generator)</li>
<li>Trigger test execution creation</li>
<li>Test execution tickets appear with all test cases included</li>
<li>QA team executes tests and tracks results</li>
<li>Results tracked in execution tickets</li>
</ol>
<h2>The Takeaway</h2>
<p><mark>Automation removes friction from workflows</mark>. By automating test execution ticket creation, QA teams can focus on actual testing instead of administrative overhead.</p>
<p>The future of QA isn’t about managing tickets—it’s about managing quality. Let automation handle the busywork.</p>
]]></content>
        <published>2025-11-02T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[The Test Case Problem: How AI Turned Hours into Minutes]]></title>
        <id>https://feyzan.netlify.app/ai-test-case-generator</id>
        <link href="https://feyzan.netlify.app/ai-test-case-generator"/>
        <updated>2025-11-01T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p><img src="https://feyzan.netlify.app/.netlify/images?url=_astro%2Ftc-gen-jira.CDYQ35sc.png&amp;fm=webp&amp;w=800&amp;h=461" alt="Jira AI Automation" /></p>
<h2>The Challenge</h2>
<p>At FrankieOne, our QA team was drowning in manual test case creation. Every feature ticket required:</p>
<ul>
<li>Reading requirements and acceptance criteria</li>
<li>Manually writing test steps (happy path, edge cases, error scenarios)</li>
<li>Creating Jira issues for each test case</li>
<li>Linking them back to the requirement</li>
</ul>
<p>For a team of 6 QA engineers handling 20+ features per sprint, this meant <strong>8-12 hours per week</strong> spent on boilerplate test case writing instead of actual testing.</p>
<p>The math was brutal:</p>
<ul>
<li>20 features × 6-10 test cases each = 120-200 test cases per sprint</li>
<li>15 minutes per test case = 15-25 hours of manual work</li>
<li>Result: <strong>QA bottleneck, delayed testing, inconsistent coverage</strong></li>
</ul>
<p>What if we could generate test cases in seconds instead of hours?</p>
<h2>The Solution</h2>
<p>I built a <mark>Python-based GitHub Action</mark> that generates test cases automatically from Jira tickets using AWS Bedrock AI. Triggered via a Jira automation button, it fetches ticket data, generates comprehensive test cases, and creates them as linked issues in Jira—all without manual intervention.</p>
<p><strong>The result:</strong> From 15 minutes per test case to 30 seconds. <strong>↓80% reduction in test case creation time.</strong></p>
<h2>How It Works</h2>
<h3>1. Fetch &amp; Prepare</h3>
<p>When triggered, the automation:</p>
<ul>
<li>Extracts ticket details (summary, description, acceptance criteria)</li>
<li>Formats data into a structured input</li>
<li>Validates completeness and clarity</li>
</ul>
<h3>2. Generate</h3>
<p>The prepared data is sent to <mark>AWS Bedrock AI</mark>, which:</p>
<ul>
<li>Analyzes requirements and acceptance criteria</li>
<li>Generates comprehensive test case definitions</li>
<li>Returns structured test scenarios covering happy paths, edge cases, and error conditions</li>
</ul>
<h3>3. Create &amp; Link</h3>
<p>Generated test cases are automatically:</p>
<ul>
<li>Created as new Jira issues (type: Test Case)</li>
<li>Populated with test steps as comments</li>
<li>Linked back to the original requirement ticket</li>
<li>Ready for QA execution</li>
</ul>
<p><img src="https://feyzan.netlify.app/.netlify/images?url=_astro%2Ftc-gen-gh-1.D0GHnx1A.png&amp;fm=webp&amp;w=800&amp;h=421" alt="Test case generation process" />
<img src="https://feyzan.netlify.app/.netlify/images?url=_astro%2Ftc-gen-gh-2.C6AbPuBn.png&amp;fm=webp&amp;w=800&amp;h=421" alt="Tickets created and linked" /></p>
<h2>The 3-Step Process</h2>
<pre><code>Requirement Ticket
       ↓
[Trigger Automation Button]
       ↓
Fetch Jira Data → Format Input → Send to Bedrock AI
       ↓
Generate Test Cases (happy path, edge cases, errors)
       ↓
Create Jira Issues → Add Test Steps → Link to Original
       ↓
Test Cases Ready for Execution
</code></pre>
<h2>Key Features</h2>
<h3>Automatic Test Case Creation</h3>
<ul>
<li><mark>No manual writing required</mark></li>
<li>Covers happy paths, edge cases, and error scenarios</li>
<li>Consistent test structure across all tickets</li>
</ul>
<h3>Seamless Jira Integration</h3>
<ul>
<li>One-click automation from ticket view</li>
<li>Test cases appear as linked issues</li>
<li>Full audit trail and traceability</li>
</ul>
<h2>Technical Architecture</h2>
<table>
<thead>
<tr>
<th>Component</th>
<th>Details</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Language</strong></td>
<td>Python</td>
</tr>
<tr>
<td><strong>Platform</strong></td>
<td>GitHub Actions</td>
</tr>
<tr>
<td><strong>Input</strong></td>
<td>Jira ticket IDs</td>
</tr>
<tr>
<td><strong>Processing</strong></td>
<td>AWS Bedrock AI (Claude 3.5 Sonnet)</td>
</tr>
<tr>
<td><strong>Output</strong></td>
<td>Linked test case issues</td>
</tr>
<tr>
<td><strong>Integration</strong></td>
<td>Jira REST API, GitHub Actions webhook</td>
</tr>
</tbody>
</table>
<h2>Implementation Overview</h2>
<p>The automation runs in three stages:</p>
<h3>Stage 1: Fetch &amp; Prepare</h3>
<pre><code class="language-python">def fetch_jira_ticket(ticket_id):
    """Fetch ticket details from Jira"""
    response = jira_client.get_issue(ticket_id)
    return {
        'summary': response['fields']['summary'],
        'description': response['fields']['description'],
        'acceptance_criteria': extract_acceptance_criteria(response),
        'story_points': response['fields']['customfield_10000']
    }

def format_for_ai(ticket_data):
    """Format ticket data for AI processing"""
    return f"""
    Feature: {ticket_data['summary']}
    Description: {ticket_data['description']}
    Acceptance Criteria:
    {ticket_data['acceptance_criteria']}
    
    Generate comprehensive test cases covering:
    1. Happy path scenarios
    2. Edge cases and boundary conditions
    3. Error handling and validation
    4. Security considerations
    """
</code></pre>
<h3>Stage 2: Generate with AI</h3>
<pre><code class="language-python">def generate_test_cases(formatted_input):
    """Use AWS Bedrock to generate test cases"""
    response = bedrock_client.invoke_model(
        modelId='anthropic.claude-3-5-sonnet-20241022-v2:0',
        body=json.dumps({
            'anthropic_version': 'bedrock-2023-06-01',
            'max_tokens': 2000,
            'messages': [{
                'role': 'user',
                'content': formatted_input
            }]
        })
    )
    return parse_test_cases(response['content'][0]['text'])
</code></pre>
<h3>Stage 3: Create &amp; Link</h3>
<pre><code class="language-python">def create_test_cases_in_jira(ticket_id, test_cases):
    """Create test case issues and link to requirement"""
    for test_case in test_cases:
        new_issue = jira_client.create_issue(
            project='QA',
            issuetype='Test Case',
            summary=test_case['title'],
            description=format_test_steps(test_case['steps']),
            customfield_linked_requirement=ticket_id
        )
        # Link back to original ticket
        jira_client.create_issue_link(
            'relates to',
            ticket_id,
            new_issue.key
        )
</code></pre>
<h2>Real-World Impact</h2>
<ul>
<li><mark><strong>Faster Coverage</strong></mark>: Test cases generated in seconds instead of hours</li>
<li><mark><strong>Consistency</strong></mark>: All test cases follow the same structure and quality standards</li>
<li><mark><strong>Traceability</strong></mark>: Every test case is linked to its requirement</li>
<li><mark><strong>Scalability</strong></mark>: Works for any number of tickets simultaneously</li>
</ul>
<h2>Benefits for QA Teams</h2>
<ol>
<li><mark><strong>More Time for Exploration</strong></mark>: QA focuses on exploratory testing instead of writing boilerplate</li>
<li><mark><strong>Better Coverage</strong></mark>: AI catches edge cases humans might miss</li>
<li><mark><strong>Reduced Errors</strong></mark>: Automated generation eliminates typos and formatting issues</li>
<li><mark><strong>Audit Ready</strong></mark>: Full traceability for compliance requirements</li>
</ol>
<h2>Lessons Learned</h2>
<h3>1. Prompt Engineering Matters</h3>
<p>The quality of generated test cases depends heavily on how you format the input. We iterated on the prompt multiple times:</p>
<ul>
<li><strong>First attempt</strong>: Generic prompt → Generic test cases</li>
<li><strong>Second attempt</strong>: Added acceptance criteria context → Better coverage</li>
<li><strong>Final version</strong>: Included examples of good test cases → 90%+ usable output</li>
</ul>
<h3>2. AI Isn’t Perfect (But It’s Good Enough)</h3>
<p>Generated test cases aren’t always perfect. We found:</p>
<ul>
<li>~85% of generated test cases are immediately usable</li>
<li>~10% need minor tweaks</li>
<li>~5% need to be rewritten</li>
</ul>
<p>This is still a <strong>massive time savings</strong> compared to writing from scratch.</p>
<h3>3. Integration Complexity</h3>
<p>The hardest part wasn’t the AI—it was integrating with Jira reliably:</p>
<ul>
<li>Handling API rate limits</li>
<li>Managing authentication securely</li>
<li>Ensuring idempotency (don’t create duplicates)</li>
<li>Proper error handling and logging</li>
</ul>
<h3>4. Team Adoption</h3>
<p>Getting QA to trust AI-generated test cases took time:</p>
<ul>
<li>Started with opt-in approach</li>
<li>Showed metrics and quality improvements</li>
<li>Built feedback loop to improve prompts</li>
<li>Now it’s standard practice</li>
</ul>
<h2>Metrics After 6 Months</h2>
<table>
<thead>
<tr>
<th>Metric</th>
<th>Before</th>
<th>After</th>
<th>Change</th>
</tr>
</thead>
<tbody>
<tr>
<td>Test cases per sprint</td>
<td>60-100</td>
<td>60-100</td>
<td>Same</td>
</tr>
<tr>
<td>Time per test case</td>
<td>15 min</td>
<td>30 sec</td>
<td><strong>↓98%</strong></td>
</tr>
<tr>
<td>QA time on test creation</td>
<td>15-25 hrs/week</td>
<td>2-3 hrs/week</td>
<td><strong>↓85%</strong></td>
</tr>
<tr>
<td>Test case consistency</td>
<td>60%</td>
<td>95%</td>
<td><strong>↑58%</strong></td>
</tr>
<tr>
<td>Coverage of edge cases</td>
<td>70%</td>
<td>92%</td>
<td><strong>↑31%</strong></td>
</tr>
<tr>
<td>Bugs found in testing</td>
<td>45/sprint</td>
<td>52/sprint</td>
<td><strong>↑15%</strong></td>
</tr>
</tbody>
</table>
<p>The last metric is the most important: <strong>QA found more bugs</strong> because they had time for exploratory testing instead of writing boilerplate.</p>
<h2>The Takeaway</h2>
<p><mark>Intelligent automation transforms how teams work</mark>. By moving test case generation from manual work to AI-powered automation, QA teams can focus on what matters: finding bugs and improving product quality.</p>
<p>The future of QA isn’t about writing more tests—it’s about generating smarter tests automatically and spending time on high-value testing activities.</p>
<p><strong>If you’re managing a QA team drowning in manual test case creation, this is worth exploring.</strong> The ROI is clear: more time for actual testing, better coverage, and happier QA engineers.</p>
]]></content>
        <published>2025-11-01T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Breaking Down Silos: The Day My Tickets Started Speaking Everyone's Language]]></title>
        <id>https://feyzan.netlify.app/ai-role-based-insights</id>
        <link href="https://feyzan.netlify.app/ai-role-based-insights"/>
        <updated>2025-11-01T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p><img src="https://feyzan.netlify.app/.netlify/images?url=_astro%2Fai-insights.C0l7aytM.png&amp;fm=webp&amp;w=800&amp;h=497" alt="Ai Role-Based Insights" /></p>
<h2>The Challenge</h2>
<p>At FrankieOne, we had a recurring problem: <strong>tickets lacked context for different stakeholders</strong>.</p>
<p>A single feature ticket needed to be understood by:</p>
<ul>
<li><strong>QA Engineers</strong>: “What edge cases should I test?”</li>
<li><strong>Product Managers</strong>: “What’s the user impact and success metric?”</li>
<li><strong>Developers</strong>: “What are the technical risks and dependencies?”</li>
<li><strong>Security Team</strong>: “Are there any compliance implications?”</li>
</ul>
<p>Without this context, we’d see:</p>
<ul>
<li>QA asking questions in comments (delays testing)</li>
<li>PMs guessing at success metrics</li>
<li>Devs discovering risks mid-implementation</li>
<li>Meetings to “align on ticket details”</li>
</ul>
<p><strong>Result:</strong> 30-60 minutes per ticket spent on context-gathering instead of actual work.</p>
<h2>The Solution</h2>
<p>I built a <mark>Python-based GitHub Action</mark> that generates <mark>role-based insights</mark> for Jira tickets using AWS Bedrock AI. With a single button click, the system analyzes ticket content and generates tailored insights for different stakeholders—all automatically posted as comments.</p>
<p><strong>The result:</strong> From 30-60 minutes of context-gathering to instant insights. <strong>↓60% reduction in context gathering time.</strong></p>
<h2>How It Works</h2>
<h3>1. Fetch Ticket Data</h3>
<p>When triggered, the automation:</p>
<ul>
<li>Extracts ticket details (summary, description, acceptance criteria)</li>
<li>Retrieves ticket context and related issues</li>
<li>Formats data for AI analysis</li>
</ul>
<h3>2. Generate Role-Based Insights</h3>
<p>The ticket data is sent to <mark>AWS Bedrock AI</mark>, which generates insights for multiple roles:</p>
<ul>
<li><strong>QA Perspective</strong>: Test scenarios, edge cases, validation points</li>
<li><strong>Product Manager Perspective</strong>: User impact, business value, success metrics</li>
<li><strong>Developer Perspective</strong>: Technical complexity, implementation risks, dependencies</li>
<li><strong>Custom Roles</strong>: Extensible for additional stakeholder perspectives</li>
</ul>
<h3>3. Post as Comments</h3>
<p>Generated insights are automatically:</p>
<ul>
<li>Formatted as structured comments on the ticket</li>
<li>Tagged with the role (e.g., “QA Insights”, “PM Insights”)</li>
<li>Ready for team discussion and reference</li>
</ul>
<h2>The 3-Step Process</h2>
<pre><code>Jira Ticket
       ↓
[Trigger Automation Button]
       ↓
Fetch Ticket Data → Analyze Content → Send to Bedrock AI
       ↓
Generate Role-Based Insights (QA, PM, Dev, etc.)
       ↓
Post Comments → Tag by Role → Ready for Discussion
       ↓
Team Has Complete Context
</code></pre>
<h2>Key Features</h2>
<h3>Multi-Perspective Analysis</h3>
<ul>
<li><mark>QA insights</mark> for testing and quality assurance</li>
<li><mark>PM insights</mark> for business and user impact</li>
<li><mark>Dev insights</mark> for technical implementation</li>
<li>Extensible for custom roles</li>
</ul>
<h3>Seamless Jira Integration</h3>
<ul>
<li>One-click automation from ticket view</li>
<li>Insights appear as structured comments</li>
<li>Full context preserved for team discussion</li>
</ul>
<h3>Consistent Quality</h3>
<ul>
<li>AI-generated insights follow the same structure</li>
<li>Standardized format across all tickets</li>
<li>Easy to scan and reference</li>
</ul>
<h2>Technical Architecture</h2>
<table>
<thead>
<tr>
<th>Component</th>
<th>Details</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Language</strong></td>
<td>Python</td>
</tr>
<tr>
<td><strong>Platform</strong></td>
<td>GitHub Actions</td>
</tr>
<tr>
<td><strong>Input</strong></td>
<td>Jira ticket IDs</td>
</tr>
<tr>
<td><strong>Processing</strong></td>
<td>AWS Bedrock AI (Claude 3.5 Sonnet)</td>
</tr>
<tr>
<td><strong>Output</strong></td>
<td>Role-based insight comments</td>
</tr>
<tr>
<td><strong>Integration</strong></td>
<td>Jira REST API, GitHub Actions webhook</td>
</tr>
</tbody>
</table>
<h2>Implementation Overview</h2>
<p>The system generates insights in three stages:</p>
<h3>Stage 1: Fetch &amp; Analyze</h3>
<pre><code class="language-python">def fetch_ticket_context(ticket_id):
    """Fetch ticket and related context"""
    ticket = jira_client.get_issue(ticket_id)
    linked_issues = jira_client.get_linked_issues(ticket_id)
    
    return {
        'ticket': ticket,
        'summary': ticket['fields']['summary'],
        'description': ticket['fields']['description'],
        'acceptance_criteria': extract_acceptance_criteria(ticket),
        'linked_stories': [i for i in linked_issues if i['type'] == 'Story'],
        'linked_bugs': [i for i in linked_issues if i['type'] == 'Bug'],
        'epic': ticket['fields'].get('customfield_epic'),
        'priority': ticket['fields']['priority']['name']
    }

def prepare_for_analysis(context):
    """Format context for AI analysis"""
    return f"""
    Ticket: {context['summary']}
    Priority: {context['priority']}
    Description: {context['description']}
    Acceptance Criteria: {context['acceptance_criteria']}
    Related Stories: {len(context['linked_stories'])}
    Related Bugs: {len(context['linked_bugs'])}
    Epic: {context['epic']}
    
    Generate insights for each role...
    """
</code></pre>
<h3>Stage 2: Generate Role-Based Insights</h3>
<pre><code class="language-python">def generate_insights(ticket_context):
    """Generate insights for multiple roles"""
    roles = ['QA', 'Product Manager', 'Developer', 'Security']
    insights = {}
    
    for role in roles:
        prompt = f"""
        Analyze this ticket from a {role} perspective:
        {ticket_context}
        
        Provide 3-5 key insights specific to {role}:
        - Focus on what matters for this role
        - Be concise and actionable
        - Highlight risks and opportunities
        """
        
        response = bedrock_client.invoke_model(
            modelId='anthropic.claude-3-5-sonnet-20241022-v2:0',
            body=json.dumps({
                'anthropic_version': 'bedrock-2023-06-01',
                'max_tokens': 500,
                'messages': [{'role': 'user', 'content': prompt}]
            })
        )
        insights[role] = response['content'][0]['text']
    
    return insights

def format_insights_for_jira(insights):
    """Format insights as structured comments"""
    comments = []
    for role, insight_text in insights.items():
        comment = f"""
        **{role} Insights**
        
        {insight_text}
        
        ---
        _Generated by AI Insights automation_
        """
        comments.append(comment)
    return comments
</code></pre>
<h3>Stage 3: Post Comments</h3>
<pre><code class="language-python">def post_insights(ticket_id, insights):
    """Post insights as comments on ticket"""
    for comment in insights:
        jira_client.add_comment(ticket_id, comment)
        # Add label for tracking
        jira_client.add_label(ticket_id, 'ai-insights-generated')
</code></pre>
<h2>Real-World Impact</h2>
<ul>
<li><mark><strong>Better Context</strong></mark>: Team members get role-specific insights without asking questions</li>
<li><mark><strong>Faster Decision Making</strong></mark>: All perspectives available immediately on the ticket</li>
<li><mark><strong>Reduced Meetings</strong></mark>: Less back-and-forth needed to understand ticket implications</li>
<li><mark><strong>Consistency</strong></mark>: Every ticket gets the same quality of analysis</li>
</ul>
<h2>Benefits for Teams</h2>
<ol>
<li><mark><strong>QA Teams</strong></mark>: Immediate test scenarios and edge cases to consider</li>
<li><mark><strong>Product Managers</strong></mark>: Clear business impact and success metrics</li>
<li><mark><strong>Developers</strong></mark>: Technical risks and implementation considerations identified upfront</li>
<li><mark><strong>Stakeholders</strong></mark>: Complete context without reading lengthy descriptions</li>
</ol>
<h2>Use Cases</h2>
<ul>
<li><strong>Feature Planning</strong>: Generate insights before development starts</li>
<li><strong>Bug Triage</strong>: Understand impact and priority from multiple angles</li>
<li><strong>Technical Debt</strong>: Assess business and technical implications</li>
<li><strong>Dependency Analysis</strong>: Identify cross-team impacts automatically</li>
</ul>
<h2>Lessons Learned</h2>
<h3>1. Context is Everything</h3>
<p>The quality of insights depends on how much context you provide:</p>
<ul>
<li><strong>Minimal context</strong> (just summary): Generic insights</li>
<li><strong>Full context</strong> (description + acceptance criteria + linked issues): Highly relevant insights</li>
<li><strong>Rich context</strong> (above + epic + priority + history): Exceptional insights</li>
</ul>
<p>We now fetch as much context as possible before generating insights.</p>
<h3>2. Role-Specific Prompts Work Better</h3>
<p>Generic prompts produce generic insights. We found that role-specific prompts are much better:</p>
<ul>
<li><strong>Generic</strong>: “Analyze this ticket”</li>
<li><strong>Role-specific</strong>: “As a QA engineer, what edge cases and test scenarios should we consider?”</li>
</ul>
<p>The second approach produces 3x more actionable insights.</p>
<h3>3. Hallucinations Are Real</h3>
<p>AI sometimes generates insights that don’t match the ticket:</p>
<ul>
<li>Mentioning features that don’t exist</li>
<li>Suggesting tests for wrong functionality</li>
<li>Misunderstanding requirements</li>
</ul>
<p><strong>Solution</strong>: Always include a review step. We have team leads review AI-generated insights before they’re posted (takes 30 seconds, prevents embarrassment).</p>
<h3>4. Different Roles Need Different Depths</h3>
<ul>
<li><strong>QA insights</strong>: Need to be very detailed (test scenarios, edge cases)</li>
<li><strong>PM insights</strong>: Need to be high-level (user impact, success metrics)</li>
<li><strong>Dev insights</strong>: Need to be technical (complexity, dependencies, risks)</li>
</ul>
<p>We tuned prompts and token limits per role.</p>
<h2>Metrics After 3 Months</h2>
<table>
<thead>
<tr>
<th>Metric</th>
<th>Before</th>
<th>After</th>
<th>Change</th>
</tr>
</thead>
<tbody>
<tr>
<td>Time to understand ticket (avg)</td>
<td>30-60 min</td>
<td>5-10 min</td>
<td><strong>↓75%</strong></td>
</tr>
<tr>
<td>Questions asked in comments</td>
<td>8-12 per ticket</td>
<td>1-2 per ticket</td>
<td><strong>↓85%</strong></td>
</tr>
<tr>
<td>Meetings to “align on ticket”</td>
<td>2-3 per sprint</td>
<td>0-1 per sprint</td>
<td><strong>↓80%</strong></td>
</tr>
<tr>
<td>Tickets with missing context</td>
<td>40%</td>
<td>5%</td>
<td><strong>↓88%</strong></td>
</tr>
<tr>
<td>Team satisfaction with clarity</td>
<td>60%</td>
<td>92%</td>
<td><strong>↑53%</strong></td>
</tr>
<tr>
<td>Time to start implementation</td>
<td>2-4 hours</td>
<td>30 min</td>
<td><strong>↓90%</strong></td>
</tr>
</tbody>
</table>
<p>The most important metric: <strong>Teams started work faster</strong> because they had all the context they needed upfront.</p>
<h2>Real Example</h2>
<p><strong>Before AI Insights:</strong></p>
<pre><code>Ticket: Add two-factor authentication to login flow

Developer: "What's the scope? SMS only or also email?"
PM: "Both, but SMS is priority"
QA: "What about users without phone numbers?"
Dev: "Good point. What should we do?"
PM: "Show an error and suggest email"
Security: "Wait, did we consider rate limiting?"
Dev: "No, let me add that"

Result: 45 minutes of back-and-forth, implementation delayed
</code></pre>
<p><strong>After AI Insights:</strong></p>
<pre><code>Ticket: Add two-factor authentication to login flow
[AI generates insights immediately]

QA Insights:
- Test SMS delivery (success, failure, timeout)
- Test email delivery (similar scenarios)
- Test users without phone/email (error handling)
- Test rate limiting (5 attempts per hour)

Dev Insights:
- Consider: SMS provider reliability, email delivery latency
- Risk: Rate limiting could lock out legitimate users
- Dependency: Need to update user model for phone number

PM Insights:
- Success metric: 80% of users enable 2FA within 30 days
- User impact: Adds 30 seconds to login flow
- Rollout: Gradual rollout starting with power users

Security Insights:
- Implement rate limiting (5 attempts per hour)
- Consider: Session fixation attacks
- Compliance: Ensure audit logging for all attempts

Result: All context available immediately, dev starts work in 10 minutes
</code></pre>
<h2>The Takeaway</h2>
<p><mark>AI-powered insights democratize context</mark>. By generating role-based perspectives automatically, teams can make better decisions faster and reduce the cognitive load of context-switching.</p>
<p>The future of collaboration isn’t about writing more comments—it’s about generating smarter insights automatically and letting teams focus on what matters: building great products.</p>
<p><strong>If your team spends more time asking questions than building, this is worth implementing.</strong> The ROI is immediate: faster decisions, better alignment, and happier engineers.</p>
]]></content>
        <published>2025-11-01T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Building a NAS and AI Assistant from a Used Set-Top Box]]></title>
        <id>https://feyzan.netlify.app/nas-stb</id>
        <link href="https://feyzan.netlify.app/nas-stb"/>
        <updated>2025-08-16T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Sometimes small experiments lead to big results. From an inexpensive device, a mini server was born that now works <mark>24/7</mark> at home.</p>
<p><img src="https://feyzan.netlify.app/.netlify/images?url=_astro%2Fnas-front.BUFX69iW.webp&amp;fm=webp&amp;w=800&amp;h=1422" alt="HG680P B860H Front" /> <img src="https://feyzan.netlify.app/.netlify/images?url=_astro%2Fnas-back.DiRJt1C8.webp&amp;fm=webp&amp;w=800&amp;h=1422" alt="HG680P B860H Back" /></p>
<h2>It Started with Curiosity</h2>
<p>A while back, I became interested in trying <strong>self-hosting</strong>: running my own server at home. It felt exciting to imagine all my data stored on my own device, automation running according to my needs, and <mark>not depending on third-party services</mark>.</p>
<p>The problem was, as soon as I started looking for hardware, reality hit hard:</p>
<ul>
<li><strong>Raspberry Pi</strong> - once cheap and accessible, now scarce and expensive</li>
<li><strong>Mini PC</strong> - great performance, but the price made me think twice for just an experiment</li>
</ul>
<p>Then one day, I saw a post on a Facebook marketplace: <strong>HG680P B860H</strong>, a used Android TV set-top box. Small form factor, modest specs, but there was one thing that made me stop scrolling — it had a <mark><strong>LAN port</strong></mark>.</p>
<blockquote>
<p>“If this can be modified into a server, why not try it?”</p>
</blockquote>
<p>The price was reasonable. If it failed? At least I’d gain <mark>experience and a story to tell</mark>.</p>
<h2>From Android TV to Linux</h2>
<p>Out of the box, this STB could only run Android TV. Great for streaming, but <mark>limited for server use</mark>. So, first step: <strong>replace the OS</strong>.</p>
<p>I chose <strong>Armbian</strong>, a Linux distribution optimized for ARM devices. With Armbian, I had <mark>full control via terminal</mark>, just like a mini computer.</p>
<p>The steps were straightforward:</p>
<ol>
<li>Download the <strong>Armbian image</strong> for HG680P</li>
<li>Prepare a <strong>128GB SD Card</strong> for the system and initial storage</li>
<li>Flash the image with <code>balenaEtcher</code></li>
<li>Insert the SD card into the STB, power it on, and… <mark>boot into Armbian!</mark></li>
</ol>
<blockquote>
<p>At that moment, it felt like “bringing to life” a device that was previously just a media player into a machine ready to work 24/7.</p>
</blockquote>
<h2>Installing CasaOS</h2>
<p>While I was comfortable in the terminal, I wanted a <strong>UI interface</strong>. My choice fell on <strong>CasaOS</strong>, a web dashboard for home servers and small NAS setups.</p>
<p>With CasaOS, I could:</p>
<ul>
<li><mark>Install applications</mark> via the App Store</li>
<li><mark>Monitor</mark> CPU, RAM, and storage</li>
<li><mark>Access files</mark> directly through the browser</li>
</ul>
<p>The installation was quick, and as soon as I opened the device’s IP address, a clean dashboard greeted me.</p>
<p><img src="https://feyzan.netlify.app/.netlify/images?url=_astro%2Fnas-casa.BBuvCJ7H.webp&amp;fm=webp&amp;w=800&amp;h=416" alt="CasaOS Dashboard" /></p>
<h2>Setting Up n8n for Automation</h2>
<p><strong>n8n</strong> is a flexible automation tool that allows me to create <mark>workflows without having to write code from scratch</mark>.</p>
<p>In CasaOS, the installation was as simple as:</p>
<ol>
<li>Search for <code>n8n</code> in the App Store</li>
<li>Click <strong>Install</strong></li>
<li>Wait a few minutes, then open it from the dashboard</li>
</ol>
<p><img src="https://feyzan.netlify.app/.netlify/images?url=_astro%2Fnas-n8n.RTeXZC1J.webp&amp;fm=webp&amp;w=800&amp;h=416" alt="n8n on CasaOS Store" /></p>
<h2>First Workflow: Curated AI News</h2>
<p>As an initial experiment, I wanted to create something that would <mark>immediately provide daily value</mark>. The idea that emerged: an <strong>AI news summary</strong> automatically sent to Telegram every morning.</p>
<p>The flow was simple but effective:</p>
<ol>
<li><strong>RSS Node</strong> → Fetches the latest AI news from <code>TheDecoder</code> and <code>TechCrunch</code>.</li>
<li><strong>Function Node</strong> → Filters news so only articles published in the <mark>last 24 hours</mark> make the list.</li>
<li><strong>Telegram Node</strong> → Sends the news list to my personal Telegram chat. The format is clean: article title, link, and brief summary.</li>
</ol>
<p><img src="https://feyzan.netlify.app/.netlify/images?url=_astro%2Fnas-workflow.COFeV3km.webp&amp;fm=webp&amp;w=800&amp;h=415" alt="n8n Workflow" /></p>
<p>The result? Every morning I get a notification containing a collection of the latest AI news. <mark>No more habit of opening multiple tabs</mark> just to find the latest updates — just open one chat, everything is already curated.</p>
<p>This workflow is simple, but it immediately <mark>saves time</mark> and ensures I don’t miss important information in the field I’m interested in.</p>
<p><img src="https://feyzan.netlify.app/.netlify/images?url=_astro%2Fnas-telegram.Da8S4Lux.webp&amp;fm=webp&amp;w=800&amp;h=1778" alt="Telegram Notification" /></p>
<h2>New Possibilities Unlocked</h2>
<p>After the first workflow succeeded, it felt like <mark>opening a door to a room full of new tools</mark>. Suddenly, other ideas began to emerge, and I realized this small device could do <mark>far more than I initially planned</mark>.</p>
<p>Some ideas I started thinking about:</p>
<ul>
<li>
<p>Automatic Work File Backup - Set <code>n8n</code> to monitor certain folders on my main NAS, then periodically make copies to <strong>Google Drive</strong>.</p>
</li>
<li>
<p>Public Data Scraper - <code>n8n</code> pulls data from specific sites — for example, product prices on marketplaces, event schedules, or specific news — then saves it in <strong>spreadsheet or database format</strong>. From there, I can create <mark>automatic analysis or reports</mark>.</p>
</li>
<li>
<p>Smart Home Control Dashboard - Connect this STB to smart home devices via <strong>API</strong> or <strong>MQTT broker</strong>, then create a simple UI interface to monitor room temperature, turn on lights, or control other devices.</p>
</li>
<li>
<p>Automatic Reminders - For example, pulling <strong>calendar data</strong>, combining it with <strong>weather data</strong>, then sending morning notifications with a schedule summary and weather forecast.</p>
</li>
</ul>
<h2>Lessons from This Experiment</h2>
<p>From this process, I gained several important notes — some technical, some related to device usage management:</p>
<ul>
<li>
<p>SD Card Works for Starting, but External HDD/SSD is Far More Durable</p>
<p>The <strong>128GB SD card</strong> I used was sufficient for installation and initial experiments. But for <mark>long-term use</mark>, especially if workflows start storing large data or making routine backups, an <strong>external SSD/HDD</strong> will be much safer from damage risk and have <mark>stable read-write speeds</mark>.</p>
</li>
<li>
<p>2GB RAM is Enough for Light Applications</p>
<p>With this much RAM, the STB can run <code>n8n</code>, file manager, and several background processes without problems. However, running <mark>heavy applications or multiple containers</mark> simultaneously will clearly cause performance degradation. This means I must <mark>selectively choose</mark> which applications to run and optimize workflows to avoid burdening RAM.</p>
</li>
<li>
<p>Monitor Performance to Keep the Server Running Smoothly</p>
<p>Regularly checking <strong>CPU</strong>, <strong>RAM</strong>, and <strong>storage</strong> usage is important. In CasaOS, this information can be seen from the dashboard. For more detail, I usually enter the terminal and use <code>htop</code> or <code>iotop</code> to see active processes.</p>
</li>
<li>
<p>Backup n8n Workflows Regularly</p>
<p><code>n8n</code> workflows are stored in a specific data folder. I created a <mark>weekly backup schedule</mark> for this folder to my main NAS. That way, if the SD card fails or the system needs to be reinstalled, I can <mark>restore all workflows</mark> without having to create them from scratch.</p>
</li>
</ul>
<hr />
<p>From a simple experiment, I now have a small digital assistant that’s always on standby. The <strong>HG680P B860H</strong> that was once just an STB now works tirelessly. Who knows, maybe old devices in your home can also have a new story.</p>
]]></content>
        <published>2025-08-16T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[How a Donkey Konga Drum Taught Me to Never Underestimate Nostalgia]]></title>
        <id>https://feyzan.netlify.app/donkey-konga</id>
        <link href="https://feyzan.netlify.app/donkey-konga"/>
        <updated>2025-05-15T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/0*9i61N8lHdX5OG_Pf.jpg" alt="Donkey Konga Drum" /></p>
<h3>The Beat-Up Old Dusty Drum</h3>
<p>It started as a joke, really.
Buried in a bulk shipment of Japanese action figures and vintage games was this chunky, brown &amp; white plastic drum — a Donkey Konga controller for the Nintendo GameCube. I almost tossed it aside. Who would want this outdated rhythm game accessory in 2025?</p>
<p>But hey, I listed it anyway. “For the meme,” I thought.</p>
<p>Then it sold in two days — <mark>for eight times what I’d paid</mark>.</p>
<h3>The Madness of Niche Markets</h3>
<p>Turns out Donkey Konga drums are <mark>weirdly sought-after</mark>:</p>
<ul>
<li><strong>Speedrunners</strong> needed them for record attempts</li>
<li><strong>Millennials</strong> were drowning in 2000s nostalgia</li>
<li><strong>Collectors</strong> building “obscure Nintendo peripherals” shrines</li>
</ul>
<p>This wasn’t a fluke. Since then, I’ve learned:</p>
<ol>
<li>
<p><mark>“Useless” items often have secret fanbases</mark> (See: The Hello Kitty toaster that started a bidding war)</p>
</li>
<li>
<p><mark>Nostalgia is the ultimate pricing hack</mark> (Especially for things people thought they’d outgrown)</p>
</li>
<li>
<p><mark>Never judge demand by your own tastes</mark> (My storage unit is full of “why would anyone — oh wait, sold already?”)</p>
</li>
</ol>
<h3>The Takeaway</h3>
<p>Now when I import, I don’t ask <em>“Is this valuable?”</em></p>
<p>I ask: <mark>“Does this spark someone’s joy?”</mark></p>
<p>Because in the end, <mark>we’re not selling plastic — we’re selling time machines</mark>.</p>
<p>(Even if the time machine is a goofy monkey drum… or a grease-stained Pokémon card.)</p>
]]></content>
        <published>2025-05-15T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Navigating Japan's Railway]]></title>
        <id>https://feyzan.netlify.app/japan-railway</id>
        <link href="https://feyzan.netlify.app/japan-railway"/>
        <updated>2025-03-29T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>Japan’s railway system is known for its efficiency and extensive coverage, making it the ideal mode of transportation for travelers exploring the nation. In this guide, we’ll cover everything you need to know, from getting an IC card to using Google Maps to navigate from one station to another.</p>
<h2>Get an IC Card</h2>
<p>Start your journey by getting an IC card, like ICOCA. You can buy and recharge these cards at ticket machines in most stations.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:598/format:webp/0*SX99qyIQTVCEbk2n.jpg" alt="IC Card" /><img src="https://miro.medium.com/v2/resize:fit:1050/format:webp/0*15f6QyuBjZEmoTeW" alt="IC Card Machine" /></p>
<h2>Know the Railway Network</h2>
<p>Japan has several railway companies, each with its own network of lines connecting major cities and attractions. Check out maps at stations or online to understand the routes.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/0*_yNzF2DjFxO78X8C.jpg" alt="Railway Network" /></p>
<h2>Using Google Maps</h2>
<p>Google Maps is a handy tool for navigating Japan’s railways. Here’s how:</p>
<ul>
<li>Enter your current location and destination.</li>
<li>Choose the train icon as your mode of transport.</li>
<li>Google Maps will show you routes, including transfer points, train numbers, and departure times.</li>
<li>Pay attention to train names and directions to ensure you’re on the right track.</li>
<li>Use the ‘Depart at’ or ‘Arrive by’ feature to plan your journey.</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*n7e1ce8mbiP55CQkPmILYw.png" alt="Google Maps" /></p>
<h2>Buying Tickets</h2>
<p>For some trips, you’ll need to buy tickets instead of using your IC card. Ticket vending machines in stations are easy to use and available in multiple languages.</p>
<h2>Navigating Stations</h2>
<p>Train stations in Japan can get busy. Follow these tips for a smooth experience:</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/0*KoWLIHRfDrsY4fVR.jpg" alt="Station" /></p>
<ul>
<li>Look out for signs and platform indicators.</li>
<li>Wait for passengers to get off before boarding.</li>
<li>Stand in line while waiting for trains.</li>
<li>Be considerate of others and avoid blocking paths.</li>
<li>Listen for announcements about trains and any delays.</li>
</ul>
]]></content>
        <published>2025-03-29T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[From 0 to 180+ Mentees: Building a Mentorship Program That Actually Works]]></title>
        <id>https://feyzan.netlify.app/mentoring-180-engineers</id>
        <link href="https://feyzan.netlify.app/mentoring-180-engineers"/>
        <updated>2025-03-15T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<h2>The Journey</h2>
<p>When I started my career, I had a mentor who changed my life. They didn’t just teach me code—they taught me how to think, how to grow, and how to help others.</p>
<p>Years later, I wanted to pay it forward. But I didn’t want to just mentor one person. I wanted to build something that could scale and help as many engineers as possible.</p>
<p><strong>The result:</strong> A mentorship program that has helped 180+ engineers get jobs, grow their careers, and become better engineers.</p>
<p><strong>88% job placement rate.</strong> 26k+ LinkedIn followers. AWS Community Builder.</p>
<p>Here’s how I built it and what I learned.</p>
<h2>The Problem I Solved</h2>
<p>When I was starting out, mentorship was hard to find:</p>
<ul>
<li>Senior engineers were busy</li>
<li>Mentorship was informal and inconsistent</li>
<li>No structure, no accountability</li>
<li>You had to know someone to get help</li>
</ul>
<p>I wanted to create something different: <strong>accessible, structured, scalable mentorship</strong>.</p>
<h2>The Program Structure</h2>
<h3>Phase 1: Intake &amp; Assessment (Week 1)</h3>
<p>Every mentee starts with a structured intake:</p>
<pre><code>1. Background Assessment
   - Current role/experience level
   - Goals (promotion? new job? skill building?)
   - Challenges they're facing
   - Learning style preferences

2. Goal Setting
   - 3-month goal (specific, measurable)
   - 6-month goal
   - 12-month goal

3. Baseline Assessment
   - Technical skills (1-10 scale)
   - Soft skills (communication, leadership)
   - Areas of strength
   - Areas for improvement

4. Mentorship Plan
   - Weekly meeting schedule
   - Focus areas
   - Success metrics
</code></pre>
<p>This takes 1 hour but saves months of wasted time.</p>
<h3>Phase 2: Weekly Mentoring (Weeks 2-12)</h3>
<p><strong>Weekly 1-on-1 meetings (30-60 minutes)</strong></p>
<p>Structure:</p>
<pre><code>1. Check-in (5 min)
   - How's work going?
   - Any blockers?

2. Progress Review (10 min)
   - Review goals from last week
   - Celebrate wins
   - Discuss challenges

3. Deep Dive (30 min)
   - Focus on one topic
   - Could be: code review, system design, career advice, etc.

4. Action Items (10 min)
   - What will you do this week?
   - How can I help?
   - Next week's topic
</code></pre>
<p><strong>Topics I cover:</strong></p>
<ul>
<li><strong>Technical:</strong> System design, code quality, testing, performance</li>
<li><strong>Career:</strong> Negotiation, promotions, job hunting, side projects</li>
<li><strong>Soft Skills:</strong> Communication, leadership, conflict resolution</li>
<li><strong>Mindset:</strong> Imposter syndrome, burnout, work-life balance</li>
</ul>
<h3>Phase 3: Accountability &amp; Tracking</h3>
<p>I use a simple tracking system:</p>
<pre><code>Mentee: [Name]
Goal: [3-month goal]
Status: [On track / At risk / Off track]

Week 1: [Progress]
Week 2: [Progress]
...

Metrics:
- Skills improved: [List]
- Confidence level: [1-10]
- Job applications sent: [Number]
- Interviews scheduled: [Number]
- Offers received: [Number]
</code></pre>
<p>This creates accountability and makes progress visible.</p>
<h2>What Actually Works</h2>
<h3>1. Specificity Beats Generality</h3>
<p><strong>Bad mentorship:</strong> “You should learn system design”
<strong>Good mentorship:</strong> “Let’s design a URL shortener. Here’s what I’d consider…”</p>
<p>Specific problems with real solutions stick. Generic advice is forgotten.</p>
<h3>2. Mentees Need to Do the Work</h3>
<p>I learned this the hard way. I can’t make someone successful—I can only guide them.</p>
<p><strong>My role:</strong></p>
<ul>
<li>Provide direction</li>
<li>Share experience</li>
<li>Hold them accountable</li>
<li>Celebrate wins</li>
</ul>
<p><strong>Their role:</strong></p>
<ul>
<li>Do the work</li>
<li>Ask questions</li>
<li>Apply learnings</li>
<li>Track progress</li>
</ul>
<p>The mentees who succeed are the ones who take ownership.</p>
<h3>3. Different Mentees Need Different Approaches</h3>
<p><strong>Junior engineers (0-2 years):</strong></p>
<ul>
<li>Need fundamentals (design patterns, testing, debugging)</li>
<li>Need confidence building</li>
<li>Need career guidance</li>
</ul>
<p><strong>Mid-level engineers (2-5 years):</strong></p>
<ul>
<li>Need system design skills</li>
<li>Need leadership preparation</li>
<li>Need negotiation skills</li>
</ul>
<p><strong>Senior engineers (5+ years):</strong></p>
<ul>
<li>Need strategic thinking</li>
<li>Need executive presence</li>
<li>Need mentorship skills (meta!)</li>
</ul>
<p>I tailor my approach to each person.</p>
<h3>4. Job Search is a Skill</h3>
<p>Many engineers are great at their jobs but terrible at job searching. I teach:</p>
<p><strong>Resume optimization:</strong></p>
<ul>
<li>Quantify impact (not just responsibilities)</li>
<li>Use keywords from job descriptions</li>
<li>Lead with achievements</li>
</ul>
<p><strong>Interview preparation:</strong></p>
<ul>
<li>System design practice</li>
<li>Behavioral interview coaching</li>
<li>Salary negotiation</li>
</ul>
<p><strong>Job search strategy:</strong></p>
<ul>
<li>Target companies (not spray and pray)</li>
<li>Leverage network</li>
<li>Follow up consistently</li>
</ul>
<p>This alone has helped 50+ mentees get jobs.</p>
<h3>5. Soft Skills Matter More Than You Think</h3>
<p>The best engineers I know aren’t always the smartest. They’re the ones who:</p>
<ul>
<li>Communicate clearly</li>
<li>Listen well</li>
<li>Build relationships</li>
<li>Handle conflict gracefully</li>
<li>Lead without authority</li>
</ul>
<p>I spend 30% of mentoring time on soft skills.</p>
<h2>Lessons Learned</h2>
<h3>1. Mentorship Doesn’t Scale Linearly</h3>
<p>I started mentoring 1-on-1. Then 5. Then 20. Then 50.</p>
<p>At 50 mentees, I hit a wall. I couldn’t give everyone enough attention.</p>
<p><strong>Solution:</strong> I created leverage:</p>
<ul>
<li><strong>Group sessions</strong> (office hours, workshops)</li>
<li><strong>Written content</strong> (guides, templates, frameworks)</li>
<li><strong>Peer mentoring</strong> (senior mentees mentor junior ones)</li>
<li><strong>Community</strong> (mentees help each other)</li>
</ul>
<p>Now I can support 180+ mentees with 10 hours/week of my time.</p>
<h3>2. Not Everyone Wants to Be Mentored</h3>
<p>Some people are resistant:</p>
<ul>
<li>“I don’t need help”</li>
<li>“I’ll figure it out myself”</li>
<li>“I don’t have time for meetings”</li>
</ul>
<p>I learned to recognize this early and not force it. Mentorship only works if both people are committed.</p>
<h3>3. Mentors Need Mentors Too</h3>
<p>I still have mentors. I still learn. I still get stuck.</p>
<p>Staying humble and continuing to learn is essential. My mentees can tell when I’m genuinely trying to help vs. just dispensing advice.</p>
<h3>4. Celebrate Wins (Even Small Ones)</h3>
<p>When a mentee gets a job, I celebrate with them. When they solve a hard problem, I acknowledge it.</p>
<p>This builds momentum and keeps them motivated.</p>
<h3>5. Track Everything</h3>
<p>I keep detailed notes on every mentee:</p>
<ul>
<li>Their goals</li>
<li>Their progress</li>
<li>Challenges they’ve overcome</li>
<li>Wins they’ve achieved</li>
</ul>
<p>This helps me:</p>
<ul>
<li>Remember context between meetings</li>
<li>Spot patterns</li>
<li>Measure impact</li>
<li>Improve the program</li>
</ul>
<h2>The Program Today</h2>
<p><strong>180+ mentees mentored</strong></p>
<ul>
<li>88% job placement rate</li>
<li>50+ promotions</li>
<li>30+ career transitions</li>
<li>100+ skill improvements</li>
</ul>
<p><strong>How it works:</strong></p>
<ul>
<li><strong>1-on-1 mentoring</strong> (30-40 active mentees)</li>
<li><strong>Group office hours</strong> (weekly, 20-30 people)</li>
<li><strong>Workshops</strong> (monthly, 50+ people)</li>
<li><strong>Peer mentoring</strong> (senior mentees help junior ones)</li>
<li><strong>Content library</strong> (guides, templates, frameworks)</li>
</ul>
<p><strong>Time investment:</strong> ~10 hours/week</p>
<p><strong>Impact:</strong> Helping engineers change their careers, get better jobs, and grow as people.</p>
<h2>How to Build Your Own Mentorship Program</h2>
<p>If you want to start mentoring:</p>
<h3>Start Small</h3>
<ul>
<li>Pick 1-2 people</li>
<li>Be consistent</li>
<li>Build trust</li>
<li>Then expand</li>
</ul>
<h3>Be Specific</h3>
<ul>
<li>Set clear goals</li>
<li>Track progress</li>
<li>Measure impact</li>
<li>Adjust as needed</li>
</ul>
<h3>Create Leverage</h3>
<ul>
<li>Document what you teach</li>
<li>Create group sessions</li>
<li>Build community</li>
<li>Enable peer mentoring</li>
</ul>
<h3>Stay Humble</h3>
<ul>
<li>You don’t have all the answers</li>
<li>Learn from your mentees</li>
<li>Admit mistakes</li>
<li>Keep growing</li>
</ul>
<h3>Measure Impact</h3>
<ul>
<li>Track job placements</li>
<li>Measure skill growth</li>
<li>Celebrate wins</li>
<li>Share stories</li>
</ul>
<h2>Real Stories</h2>
<p><strong>Mentee 1: Junior to Mid-Level</strong></p>
<ul>
<li>Started: Junior developer, imposter syndrome, stuck in first job</li>
<li>6 months: Learned system design, built confidence, got promoted</li>
<li>12 months: Senior developer, mentoring others</li>
</ul>
<p><strong>Mentee 2: Career Transition</strong></p>
<ul>
<li>Started: QA engineer, wanted to become a developer</li>
<li>6 months: Learned programming, built portfolio</li>
<li>12 months: Software engineer at FAANG company, 40% salary increase</li>
</ul>
<p><strong>Mentee 3: Leadership Journey</strong></p>
<ul>
<li>Started: Senior engineer, wanted to lead</li>
<li>6 months: Learned leadership skills, led a small project</li>
<li>12 months: Engineering manager, managing a team of 5</li>
</ul>
<p>These aren’t exceptions. They’re the norm in my program.</p>
<h2>The Takeaway</h2>
<p><mark>Mentorship is one of the highest-leverage activities you can do as an engineer.</mark></p>
<p>You can:</p>
<ul>
<li>Help someone change their career</li>
<li>Accelerate their growth by years</li>
<li>Build a community</li>
<li>Make a real impact</li>
</ul>
<p>And the best part? You learn as much from mentoring as your mentees do.</p>
<p><strong>If you’re a senior engineer, consider starting a mentorship program.</strong> It doesn’t have to be formal. It doesn’t have to be big. Start with one person. Be consistent. Track impact.</p>
<p>The world needs more great mentors. Be one.</p>
<hr />
<h2>Resources I Use</h2>
<ul>
<li><strong>Tracking:</strong> Notion (simple spreadsheet works too)</li>
<li><strong>Meetings:</strong> Google Meet / Zoom</li>
<li><strong>Content:</strong> Google Docs (shared templates)</li>
<li><strong>Community:</strong> Slack or Discord</li>
<li><strong>Learning:</strong> Books, courses, podcasts (I share recommendations)</li>
</ul>
<p><strong>Books I recommend to mentees:</strong></p>
<ul>
<li>“The Effective Engineer” by Edmond Lau</li>
<li>“Cracking the Coding Interview” by Gayle Laakmann McDowell</li>
<li>“Crucial Conversations” by Kerry Patterson</li>
<li>“The Manager’s Path” by Camille Fournier</li>
<li>“Radical Candor” by Kim Scott</li>
</ul>
<p><strong>If you want to start mentoring, these resources will help you get started.</strong></p>
]]></content>
        <published>2025-03-15T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Building a Test Automation Framework That Scales: Lessons from 10,000+ Tests]]></title>
        <id>https://feyzan.netlify.app/test-automation-at-scale</id>
        <link href="https://feyzan.netlify.app/test-automation-at-scale"/>
        <updated>2025-03-10T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<h2>The Challenge</h2>
<p>At TipTip, we were a fast-growing startup ($13M Series A) shipping features weekly. But as we scaled, our manual testing became a bottleneck:</p>
<ul>
<li><strong>300+ test cases</strong> per sprint, growing monthly</li>
<li><strong>3 QA engineers</strong> couldn’t keep up with feature velocity</li>
<li><strong>Regression testing</strong> took 2-3 days per release</li>
<li><strong>Bug escapes</strong> were increasing (things breaking in production)</li>
</ul>
<p>We needed test automation, but not just any automation. We needed something that could:</p>
<ol>
<li>Scale to thousands of tests</li>
<li>Run in parallel without flaking</li>
<li>Catch real bugs (not just false positives)</li>
<li>Be maintainable by a small team</li>
</ol>
<h2>The Solution: TipTip Automation Framework</h2>
<p>I built an enterprise-grade test automation framework using <strong>Ruby, Selenium, Cucumber, and Jenkins</strong>. The framework handled:</p>
<ul>
<li><strong>Web testing</strong> (desktop and mobile browsers)</li>
<li><strong>Mobile app testing</strong> (iOS and Android)</li>
<li><strong>API testing</strong> (REST and GraphQL)</li>
<li><strong>Visual regression testing</strong> (pixel-perfect comparisons)</li>
<li><strong>Parallel execution</strong> (100+ tests simultaneously)</li>
</ul>
<p><strong>The result:</strong> ↑90% automation coverage, ↓85% regression effort, ↓90% bug escapes.</p>
<h2>Architecture Overview</h2>
<h3>Core Components</h3>
<pre><code>┌─────────────────────────────────────────────────────┐
│         Cucumber Feature Files (BDD)                │
│  (Written in plain English, not code)               │
└────────────────┬────────────────────────────────────┘
                 │
┌────────────────▼────────────────────────────────────┐
│      Cucumber Step Definitions (Ruby)               │
│  (Maps English to actual test code)                 │
└────────────────┬────────────────────────────────────┘
                 │
┌────────────────▼────────────────────────────────────┐
│    Page Object Model (Selenium WebDriver)           │
│  (Encapsulates UI interactions)                     │
└────────────────┬────────────────────────────────────┘
                 │
┌────────────────▼────────────────────────────────────┐
│         Jenkins CI/CD Pipeline                      │
│  (Runs tests in parallel, generates reports)        │
└─────────────────────────────────────────────────────┘
</code></pre>
<h3>Key Design Patterns</h3>
<p><strong>1. Page Object Model</strong></p>
<p>Instead of scattered selectors throughout tests, we centralized all UI interactions:</p>
<pre><code class="language-ruby"># pages/login_page.rb
class LoginPage
  def initialize(driver)
    @driver = driver
  end

  def enter_email(email)
    @driver.find_element(:id, 'email').send_keys(email)
  end

  def enter_password(password)
    @driver.find_element(:id, 'password').send_keys(password)
  end

  def click_login
    @driver.find_element(:xpath, '//button[text()="Login"]').click
  end

  def is_logged_in?
    @driver.find_element(:id, 'user-menu').displayed?
  end
end

# features/login.feature
Feature: User Login
  Scenario: Successful login
    Given I am on the login page
    When I enter email "user@example.com"
    And I enter password "secure123"
    And I click login
    Then I should be logged in
</code></pre>
<p><strong>Benefits:</strong></p>
<ul>
<li>Selectors in one place (easy to update when UI changes)</li>
<li>Tests read like documentation</li>
<li>Reusable across multiple tests</li>
<li>Non-technical people can write tests</li>
</ul>
<p><strong>2. Parallel Execution</strong></p>
<p>Running 300 tests sequentially took 8 hours. Running them in parallel took 30 minutes.</p>
<pre><code class="language-ruby"># config/parallel.yml
parallel:
  workers: 10
  timeout: 300
  retry_count: 2
  
# Jenkins pipeline
stage('Test') {
  parallel {
    stage('Smoke Tests') {
      steps { sh 'bundle exec cucumber features/smoke/' }
    }
    stage('Regression Tests') {
      steps { sh 'bundle exec cucumber features/regression/' }
    }
    stage('API Tests') {
      steps { sh 'bundle exec cucumber features/api/' }
    }
    stage('Visual Tests') {
      steps { sh 'bundle exec cucumber features/visual/' }
    }
  }
}
</code></pre>
<p><strong>Key insight:</strong> Parallel execution is only useful if tests are independent. We had to refactor tests to:</p>
<ul>
<li>Use isolated test data</li>
<li>Clean up after each test</li>
<li>Avoid shared state</li>
</ul>
<p><strong>3. Visual Regression Testing</strong></p>
<p>Catching UI bugs automatically:</p>
<pre><code class="language-ruby"># features/step_definitions/visual_steps.rb
When('I take a screenshot of the dashboard') do
  @driver.save_screenshot('dashboard.png')
end

Then('the dashboard should match the baseline') do
  baseline = 'baselines/dashboard.png'
  current = 'screenshots/dashboard.png'
  
  diff = ImageCompare.compare(baseline, current)
  expect(diff.pixels_changed).to be &lt; 10  # Allow 10 pixel differences
end
</code></pre>
<p>This caught subtle CSS bugs that manual testing missed.</p>
<h2>Lessons Learned</h2>
<h3>1. Flaky Tests Are Worse Than No Tests</h3>
<p>We started with 500 tests, but 30% were flaky (failed randomly). This destroyed team trust:</p>
<ul>
<li>“Is it a real bug or just a flaky test?”</li>
<li>“Let’s just re-run it”</li>
<li>“I’ll ignore this failure”</li>
</ul>
<p><strong>Solution:</strong> We implemented:</p>
<ul>
<li>Explicit waits instead of sleep()</li>
<li>Retry logic for network failures</li>
<li>Detailed logging for debugging</li>
<li>Quarantine for flaky tests</li>
</ul>
<p>After 3 months, flakiness dropped to &lt;2%.</p>
<h3>2. Test Data Management Is Hard</h3>
<p>Tests need data to work with. We tried three approaches:</p>
<p><strong>Approach 1: Shared test database</strong></p>
<ul>
<li>❌ Tests interfere with each other</li>
<li>❌ Hard to debug</li>
<li>❌ Slow to set up</li>
</ul>
<p><strong>Approach 2: Fresh database per test</strong></p>
<ul>
<li>✅ Tests are isolated</li>
<li>❌ Slow (database setup takes time)</li>
<li>❌ Doesn’t catch data migration bugs</li>
</ul>
<p><strong>Approach 3: Hybrid (what we settled on)</strong></p>
<ul>
<li>✅ Fresh database per test suite</li>
<li>✅ Shared data within suite (faster)</li>
<li>✅ Clean up after suite completes</li>
</ul>
<pre><code class="language-ruby"># features/support/hooks.rb
Before(:suite) do
  DatabaseCleaner.strategy = :transaction
  DatabaseCleaner.clean_with(:truncation)
  create_test_data
end

After(:scenario) do
  DatabaseCleaner.clean
end
</code></pre>
<h3>3. Maintenance Is the Real Cost</h3>
<p>Writing tests is easy. Maintaining them is hard.</p>
<p><strong>Problem:</strong> Every time the UI changed, 50+ tests broke.</p>
<p><strong>Solution:</strong> We invested in:</p>
<ul>
<li>Strong Page Object Model (centralized selectors)</li>
<li>Regular refactoring (removing duplication)</li>
<li>Test documentation (why each test exists)</li>
<li>Owner assignment (each test has a maintainer)</li>
</ul>
<p>This reduced maintenance time from 4 hours/week to 1 hour/week.</p>
<h3>4. Not Everything Should Be Automated</h3>
<p>We tried to automate everything. Mistake.</p>
<p>Some tests are better manual:</p>
<ul>
<li><strong>Complex user journeys</strong> (too many edge cases)</li>
<li><strong>Exploratory testing</strong> (finding unexpected bugs)</li>
<li><strong>Usability testing</strong> (does it feel good?)</li>
</ul>
<p>We settled on:</p>
<ul>
<li><strong>Automate:</strong> Happy paths, edge cases, regressions</li>
<li><strong>Manual:</strong> Exploratory, usability, complex scenarios</li>
</ul>
<p>This gave us 90% coverage with 50% less maintenance burden.</p>
<h2>Metrics After 12 Months</h2>
<table>
<thead>
<tr>
<th>Metric</th>
<th>Before</th>
<th>After</th>
<th>Change</th>
</tr>
</thead>
<tbody>
<tr>
<td>Test automation coverage</td>
<td>10%</td>
<td>90%</td>
<td><strong>↑800%</strong></td>
</tr>
<tr>
<td>Regression testing time</td>
<td>2-3 days</td>
<td>30 min</td>
<td><strong>↓85%</strong></td>
</tr>
<tr>
<td>Bug escapes to production</td>
<td>8-12/sprint</td>
<td>1-2/sprint</td>
<td><strong>↓85%</strong></td>
</tr>
<tr>
<td>QA team size</td>
<td>3 engineers</td>
<td>3 engineers</td>
<td>Same</td>
</tr>
<tr>
<td>Features shipped/sprint</td>
<td>8-10</td>
<td>15-20</td>
<td><strong>↑75%</strong></td>
</tr>
<tr>
<td>Test maintenance time</td>
<td>4 hrs/week</td>
<td>1 hr/week</td>
<td><strong>↓75%</strong></td>
</tr>
<tr>
<td>Test flakiness</td>
<td>30%</td>
<td>&lt;2%</td>
<td><strong>↓93%</strong></td>
</tr>
</tbody>
</table>
<p><strong>Most important:</strong> With the same team size, we shipped 2x more features with better quality.</p>
<h2>Real Impact</h2>
<p><strong>Before automation:</strong></p>
<ul>
<li>Sprint: 10 features planned</li>
<li>QA spends 3 days on regression testing</li>
<li>2 bugs escape to production</li>
<li>Team ships 8-10 features</li>
</ul>
<p><strong>After automation:</strong></p>
<ul>
<li>Sprint: 20 features planned</li>
<li>QA spends 30 minutes on regression testing</li>
<li>1 bug escapes to production</li>
<li>Team ships 15-20 features</li>
<li>QA has time for exploratory testing</li>
</ul>
<h2>Key Takeaways</h2>
<ol>
<li>
<p><strong>Automation is a multiplier, not a replacement</strong></p>
<ul>
<li>It doesn’t replace good QA thinking</li>
<li>It frees QA to do higher-value work</li>
</ul>
</li>
<li>
<p><strong>Start with the right architecture</strong></p>
<ul>
<li>Page Object Model saves months of refactoring</li>
<li>Parallel execution requires independent tests</li>
<li>Test data management is critical</li>
</ul>
</li>
<li>
<p><strong>Flaky tests destroy trust</strong></p>
<ul>
<li>Better to have 100 reliable tests than 500 flaky ones</li>
<li>Invest in stability from day one</li>
</ul>
</li>
<li>
<p><strong>Maintenance is the real cost</strong></p>
<ul>
<li>Plan for it from the start</li>
<li>Invest in good architecture</li>
<li>Assign owners to tests</li>
</ul>
</li>
<li>
<p><strong>Not everything should be automated</strong></p>
<ul>
<li>Automate what’s repetitive</li>
<li>Keep humans for exploratory work</li>
<li>Balance is key</li>
</ul>
</li>
</ol>
<h2>The Takeaway</h2>
<p><mark>Test automation at scale isn’t about writing more tests—it’s about building a sustainable system that catches bugs, enables faster shipping, and keeps your team sane.</mark></p>
<p>If you’re managing a QA team and struggling with regression testing, this framework approach is worth exploring. The ROI is massive: better quality, faster shipping, and happier engineers.</p>
<p><strong>Want to build something similar?</strong> Start with Page Object Model, add parallel execution, then layer in visual testing. Don’t try to do everything at once.</p>
]]></content>
        <published>2025-03-10T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Automating Biometric Tests with Mocked Webcam Using Playwright]]></title>
        <id>https://feyzan.netlify.app/playwright-biometric-testing</id>
        <link href="https://feyzan.netlify.app/playwright-biometric-testing"/>
        <updated>2024-09-19T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<h2>The Problem</h2>
<p>Testing biometric features—facial recognition, liveness detection, document verification—requires real webcam input. But in automated testing environments, you can’t rely on:</p>
<ul>
<li>Physical webcams in CI/CD pipelines</li>
<li>Consistent video input across test runs</li>
<li>Reproducible liveness scenarios (head turns, blinks, specific angles)</li>
</ul>
<p>This creates a testing gap: <mark>biometric features often go untested in automation</mark>, forcing teams to rely on manual testing or skip coverage entirely.</p>
<p>What if you could simulate webcam input with predefined video files and automate biometric tests end-to-end?</p>
<h2>The Solution</h2>
<p>Using <mark>Playwright’s media stream mocking capabilities</mark>, you can inject predefined video files (in y4m format) as fake webcam input. Combined with Chromium’s media device simulation flags, this enables fully automated biometric testing without physical hardware.</p>
<p><strong>The result:</strong> Reproducible, deterministic biometric tests that run in CI/CD pipelines.</p>
<h2>How It Works</h2>
<p>The approach uses three key components:</p>
<ol>
<li><strong>FFmpeg</strong>: Convert media files to y4m format (YUV4MPEG)</li>
<li><strong>Chromium Flags</strong>: Enable fake media stream and device simulation</li>
<li><strong>Playwright</strong>: Orchestrate the test and interact with the application</li>
</ol>
<pre><code>Video File (MP4, MOV, etc.)
       ↓
[FFmpeg Conversion]
       ↓
y4m Format (YUV4MPEG)
       ↓
[Chromium Fake Device]
       ↓
Playwright Test → Application receives mocked webcam
       ↓
Biometric Test Completes
</code></pre>
<h2>Step-by-Step Implementation</h2>
<h3>1. Install Dependencies</h3>
<pre><code class="language-bash">npm init -y
npm install @playwright/test
</code></pre>
<p>Install FFmpeg for media conversion:</p>
<p><strong>macOS:</strong></p>
<pre><code class="language-bash">brew install ffmpeg
</code></pre>
<p><strong>Linux (Ubuntu):</strong></p>
<pre><code class="language-bash">sudo apt update
sudo apt install ffmpeg
</code></pre>
<p><strong>Windows:</strong> Download from <a href="https://ffmpeg.org/download.html">ffmpeg.org</a></p>
<h3>2. Convert Media to y4m Format</h3>
<p><mark>Playwright requires video in y4m (YUV4MPEG) format</mark> for webcam simulation. Convert your media files using FFmpeg:</p>
<pre><code class="language-bash">ffmpeg -i liveness.mp4 -pix_fmt yuv420p -f yuv4mpegpipe liveness.y4m
</code></pre>
<p><strong>Flag breakdown:</strong></p>
<ul>
<li><code>-i liveness.mp4</code>: Input video file</li>
<li><code>-pix_fmt yuv420p</code>: Convert to YUV 4:2:0 pixel format (required by Playwright)</li>
<li><code>-f yuv4mpegpipe</code>: Output as YUV4MPEG format</li>
</ul>
<p><strong>Example scenarios you might convert:</strong></p>
<ul>
<li><code>liveness-head-turn.mp4</code> → <code>liveness-head-turn.y4m</code> (head movement detection)</li>
<li><code>passport-photo.mp4</code> → <code>passport-photo.y4m</code> (document verification)</li>
<li><code>blink-sequence.mp4</code> → <code>blink-sequence.y4m</code> (blink detection)</li>
</ul>
<h3>3. Create the Test Script</h3>
<p>Create <code>biometric-test.spec.js</code>:</p>
<pre><code class="language-javascript">const { test, chromium } = require('@playwright/test');
const path = require('path');

test('should simulate liveness detection with mocked webcam', async () =&gt; {
  // Path to converted y4m video file
  const fakeVideoPath = path.resolve(__dirname, 'liveness-head-turn.y4m');

  // Launch browser with media stream mocking
  const browser = await chromium.launch({
    headless: false,
    args: [
      // Automatically grant webcam/microphone permissions
      '--use-fake-ui-for-media-stream',
      
      // Enable fake media device simulation
      '--use-fake-device-for-media-stream',
      
      // Use predefined video file as webcam source
      `--use-file-for-fake-video-capture=${fakeVideoPath}`,
    ],
  });

  const page = await browser.newPage();
  
  // Navigate to your biometric test application
  await page.goto('https://your-biometric-app.com/liveness-check');
  
  // Interact with the application
  await page.locator('button:has-text("Start Liveness Check")').click();
  
  // Wait for biometric processing (adjust timing as needed)
  await page.waitForTimeout(6000);
  
  // Verify success state
  const successMessage = await page.locator('text=Liveness verified').isVisible();
  if (successMessage) {
    console.log('✓ Liveness detection passed');
  }
  
  await browser.close();
});
</code></pre>
<h3>4. Understanding the Chromium Flags</h3>
<table>
<thead>
<tr>
<th>Flag</th>
<th>Purpose</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>--use-fake-ui-for-media-stream</code></td>
<td>Automatically grants webcam/microphone permissions without user interaction</td>
</tr>
<tr>
<td><code>--use-fake-device-for-media-stream</code></td>
<td>Enables fake media device simulation instead of using real hardware</td>
</tr>
<tr>
<td><code>--use-file-for-fake-video-capture=&lt;path&gt;</code></td>
<td>Specifies the y4m video file to use as fake webcam input</td>
</tr>
</tbody>
</table>
<p>These flags work together to create a <mark>fully simulated media environment</mark> where the browser believes it’s accessing a real webcam, but is actually playing back your predefined video.</p>
<h3>5. Run the Test</h3>
<pre><code class="language-bash">npx playwright test biometric-test.spec.js
</code></pre>
<p>Or with specific configuration:</p>
<pre><code class="language-bash">npx playwright test biometric-test.spec.js --headed --project=chromium
</code></pre>
<h2>Advanced Scenarios</h2>
<h3>Multiple Liveness Checks</h3>
<p>Test different liveness scenarios in sequence:</p>
<pre><code class="language-javascript">const scenarios = [
  { name: 'head-turn', file: 'liveness-head-turn.y4m' },
  { name: 'blink', file: 'liveness-blink.y4m' },
  { name: 'smile', file: 'liveness-smile.y4m' },
];

for (const scenario of scenarios) {
  const fakeVideoPath = path.resolve(__dirname, scenario.file);
  
  const browser = await chromium.launch({
    args: [
      '--use-fake-ui-for-media-stream',
      '--use-fake-device-for-media-stream',
      `--use-file-for-fake-video-capture=${fakeVideoPath}`,
    ],
  });
  
  // Run test with this scenario
  // ...
  
  await browser.close();
}
</code></pre>
<h3>Document Verification Testing</h3>
<p>Test passport/ID verification with static images converted to video:</p>
<pre><code class="language-bash"># Convert image sequence to video
ffmpeg -framerate 1 -i document_%03d.jpg -c:v libx264 -pix_fmt yuv420p document.mp4

# Convert to y4m
ffmpeg -i document.mp4 -pix_fmt yuv420p -f yuv4mpegpipe document.y4m
</code></pre>
<h3>Parallel Testing</h3>
<p>Run multiple biometric tests in parallel:</p>
<pre><code class="language-javascript">test.describe.parallel('Biometric Tests', () =&gt; {
  test('liveness detection', async () =&gt; { /* ... */ });
  test('document verification', async () =&gt; { /* ... */ });
  test('facial recognition', async () =&gt; { /* ... */ });
});
</code></pre>
<h2>Real-World Use Cases</h2>
<ul>
<li><strong>Liveness Detection</strong>: Verify head movement, blinks, or facial expressions</li>
<li><strong>Document Verification</strong>: Test ID/passport scanning workflows</li>
<li><strong>Facial Recognition</strong>: Validate face matching algorithms</li>
<li><strong>KYC/AML Compliance</strong>: Automate identity verification testing</li>
<li><strong>Multi-factor Authentication</strong>: Test biometric MFA flows</li>
<li><strong>Mobile App Testing</strong>: Simulate webcam on mobile browsers</li>
</ul>
<h2>Troubleshooting</h2>
<h3>Video Not Playing</h3>
<p><strong>Issue:</strong> Chromium doesn’t recognize the y4m file</p>
<p><strong>Solution:</strong> Verify conversion with FFmpeg:</p>
<pre><code class="language-bash">ffmpeg -i liveness.y4m -f null -
</code></pre>
<h3>Permission Errors</h3>
<p><strong>Issue:</strong> Browser still prompts for permissions</p>
<p><strong>Solution:</strong> Ensure <code>--use-fake-ui-for-media-stream</code> is included in launch args</p>
<h3>Timing Issues</h3>
<p><strong>Issue:</strong> Test completes before biometric processing finishes</p>
<p><strong>Solution:</strong> Adjust wait time or use explicit waits:</p>
<pre><code class="language-javascript">// Wait for specific element instead of fixed time
await page.waitForSelector('[data-testid="verification-complete"]', { timeout: 10000 });
</code></pre>
<h3>CI/CD Pipeline Issues</h3>
<p><strong>Issue:</strong> Tests fail in headless mode</p>
<p><strong>Solution:</strong> Add <code>headless: false</code> or use <code>xvfb-run</code> on Linux:</p>
<pre><code class="language-bash">xvfb-run -a npx playwright test
</code></pre>
<h2>Performance Considerations</h2>
<ul>
<li><strong>Video File Size</strong>: Keep y4m files under 100MB for CI/CD efficiency</li>
<li><strong>Test Duration</strong>: Biometric processing typically takes 3-10 seconds</li>
<li><strong>Parallel Execution</strong>: Each test needs its own browser instance (memory intensive)</li>
<li><strong>Caching</strong>: Store converted y4m files in version control or cache layer</li>
</ul>
<h2>Limitations &amp; Workarounds</h2>
<table>
<thead>
<tr>
<th>Limitation</th>
<th>Workaround</th>
</tr>
</thead>
<tbody>
<tr>
<td>Only works with Chromium</td>
<td>Use Chromium for biometric tests, other browsers for general testing</td>
</tr>
<tr>
<td>Requires y4m format</td>
<td>Always convert media files with FFmpeg first</td>
</tr>
<tr>
<td>No audio simulation</td>
<td>Use <code>--use-fake-device-for-media-stream</code> for audio mocking if needed</td>
</tr>
<tr>
<td>Deterministic only</td>
<td>Video plays the same way every time (good for testing, not for randomness)</td>
</tr>
</tbody>
</table>
<h2>Best Practices</h2>
<ol>
<li><strong>Version Control Media Files</strong>: Store y4m files in a dedicated <code>/fixtures</code> directory</li>
<li><strong>Document Scenarios</strong>: Clearly label what each video file represents</li>
<li><strong>Modularize Tests</strong>: Create reusable test functions for common biometric flows</li>
<li><strong>Monitor Performance</strong>: Track test duration to catch regressions</li>
<li><strong>Combine with Visual Testing</strong>: Use Playwright’s screenshot/video recording for debugging</li>
<li><strong>Test Failure Paths</strong>: Create videos that simulate failed biometric checks too</li>
</ol>
<h2>Example: Complete Test Suite</h2>
<pre><code class="language-javascript">const { test, expect, chromium } = require('@playwright/test');
const path = require('path');

const getVideoPath = (scenario) =&gt; 
  path.resolve(__dirname, `fixtures/${scenario}.y4m`);

const launchWithVideo = async (videoPath) =&gt; {
  return chromium.launch({
    headless: false,
    args: [
      '--use-fake-ui-for-media-stream',
      '--use-fake-device-for-media-stream',
      `--use-file-for-fake-video-capture=${videoPath}`,
    ],
  });
};

test.describe('Biometric Verification', () =&gt; {
  test('should pass liveness check with head turn', async () =&gt; {
    const browser = await launchWithVideo(getVideoPath('liveness-head-turn'));
    const page = await browser.newPage();
    
    await page.goto('https://your-app.com/verify');
    await page.click('button:has-text("Start")');
    
    await expect(page.locator('text=Verified')).toBeVisible({ timeout: 10000 });
    
    await browser.close();
  });

  test('should verify document with passport image', async () =&gt; {
    const browser = await launchWithVideo(getVideoPath('passport-scan'));
    const page = await browser.newPage();
    
    await page.goto('https://your-app.com/document-verify');
    await page.click('button:has-text("Scan Document")');
    
    await expect(page.locator('[data-testid="document-verified"]')).toBeVisible();
    
    await browser.close();
  });
});
</code></pre>
<h2>The Takeaway</h2>
<p><mark>Mocking webcam input with Playwright transforms biometric testing</mark> from a manual, environment-dependent process into a reliable, automated workflow. By converting media files to y4m format and leveraging Chromium’s media simulation, you can:</p>
<ul>
<li><strong>Automate biometric verification</strong> in CI/CD pipelines</li>
<li><strong>Ensure reproducibility</strong> with predefined video scenarios</li>
<li><strong>Reduce manual testing</strong> burden on QA teams</li>
<li><strong>Catch regressions</strong> early in the development cycle</li>
</ul>
<p>This approach is particularly valuable for fintech, identity verification, and healthcare applications where biometric testing is critical but traditionally difficult to automate.</p>
<p><strong>Start with a simple liveness test, then expand to cover your full biometric workflow.</strong></p>
]]></content>
        <published>2024-09-19T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Easy Guide to WebAssembly & Emscripten]]></title>
        <id>https://feyzan.netlify.app/emscripten</id>
        <link href="https://feyzan.netlify.app/emscripten"/>
        <updated>2024-03-17T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p><mark>WebAssembly (Wasm)</mark> has emerged as a powerful tool for bringing high-performance, low-level code to the web. With its ability to <mark>run at near-native speed</mark> in web browsers, WebAssembly opens up exciting possibilities for web development. In this tutorial, we’ll explore how to leverage <mark>Emscripten</mark>, a popular toolchain for compiling C/C++ code to WebAssembly.</p>
<h3>What is WebAssembly?</h3>
<blockquote>
<p>WebAssembly is a <mark>binary instruction format</mark> that serves as a compilation target for programming languages. It allows developers to run code written in languages like C, C++, and Rust on the web with <mark>near-native performance</mark>.</p>
</blockquote>
<p><strong>Key characteristics:</strong></p>
<ul>
<li>Unlike JavaScript, WebAssembly is <strong>not human-readable</strong></li>
<li>Designed to be <strong>compact and efficient</strong> for transmission over the network</li>
</ul>
<h3>What is Emscripten?</h3>
<blockquote>
<p>Emscripten is an <mark>open-source toolchain</mark> for compiling C and C++ code into WebAssembly, along with accompanying JavaScript glue code to integrate with web applications.</p>
</blockquote>
<p>It provides a <strong>bridge</strong> between these low-level languages and the web platform, enabling developers to:</p>
<ul>
<li>Port existing codebases to the web</li>
<li>Write new high-performance code for web deployment</li>
</ul>
<h3>Getting Started:</h3>
<p><strong>1. Install Emscripten</strong></p>
<p>The first step is to install Emscripten on your development machine. You can follow the installation instructions provided in the official Emscripten documentation (<a href="https://emscripten.org/docs/getting_started/index.html">https://emscripten.org/docs/getting_started/index.html</a>).</p>
<p><strong>2. Write Your C/C++ Code</strong></p>
<p>Once Emscripten is installed, you can start writing your C or C++ code. For demonstration purposes, let’s create a simple “Hello, World!” program in C.</p>
<pre><code class="language-c">#include &lt;stdio.h&gt;
int main() {
    printf("Hello, WebAssembly!\n");
    return 0;
}
</code></pre>
<p><strong>3. Compile to WebAssembly</strong></p>
<p>Use the Emscripten compiler (<mark><code>emcc</code></mark>) to compile your C/C++ code to WebAssembly. Navigate to the directory containing your source code and run the following command:</p>
<pre><code class="language-bash">emcc hello.c -o hello.html
</code></pre>
<blockquote>
<p>This command will generate three files: <code>hello.html</code>, <code>hello.js</code>, and <code>hello.wasm</code></p>
</blockquote>
<p><strong>4. Test Your Application with <code>emrun</code></strong></p>
<p>Instead of manually opening the HTML file in a browser, you can use <mark><code>emrun</code></mark> to simplify the testing process. First, navigate to the directory containing your <code>hello.html</code> file, then run:</p>
<pre><code class="language-bash">emrun --no_browser --port 8080 .
</code></pre>
<p>This command will start a local web server. You can then open your browser and go to <code>http://localhost:8080/hello.html</code> to see <strong>“Hello, WebAssembly!”</strong> printed!</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*d0GOnxm7YOgRNnmiOLfpOA.png" alt="Hello WebAssembly printed" /></p>
]]></content>
        <published>2024-03-17T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Handling Amazon S3 CORS Errors: A Simple Guide]]></title>
        <id>https://feyzan.netlify.app/s3-cors-error</id>
        <link href="https://feyzan.netlify.app/s3-cors-error"/>
        <updated>2024-03-03T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*bWfUIwXAwTPSMRwCYCE1vg.png" alt="S3 CORS Error" /></p>
<p>Did you encounter a CORS error while trying to download files from your Amazon S3 bucket? Don’t worry, I’ve got your back. Here’s how to understand and fix S3 CORS errors step by step:</p>
<h3>Understanding CORS:</h3>
<p>Cross-Origin Resource Sharing (CORS) is a browser security feature that prevents scripts from one domain from accessing resources on another domain. <mark>It’s a vital security measure</mark>, btw.</p>
<h3>The Problem:</h3>
<p>You’ll run into CORS errors when <mark>your S3 bucket’s CORS configuration isn’t set up properly</mark>. These errors pop up when you’re trying to access resources from a different origin or domain (see the above screenshot).</p>
<h3>Fixing CORS Errors in Amazon S3:</h3>
<p>Here’s how to solve those CORS errors step by step:</p>
<ul>
<li>
<p>Head to the AWS Management Console: Log in to your AWS Management Console and find your way to the S3 dashboard.</p>
</li>
<li>
<p>Pick Your Bucket: Select the S3 bucket that’s giving you trouble with CORS.</p>
</li>
<li>
<p>Adjust the CORS Settings: In your bucket’s properties or permissions section, go to the CORS configuration settings.</p>
</li>
</ul>
<p><img src="https://miro.medium.com/v2/format:webp/1*z_j72UlBBtzlfRSjVKrRxw.png" alt="captionless image" /></p>
<ul>
<li>Add CORS Rules: <mark>Add rules that specify which domains can access your bucket’s resources</mark>, along with the allowed HTTP methods and headers. Here’s a sample CORS setup:</li>
</ul>
<pre><code class="language-json">[
    {
        "AllowedHeaders": ["*"],
        "AllowedMethods": ["PUT", "POST", "DELETE"],
        "AllowedOrigins": ["http://www.your-domain-1.com"],
        "ExposeHeaders": []
    },
    {
        "AllowedHeaders": ["*"],
        "AllowedMethods": ["PUT", "POST", "DELETE"],
        "AllowedOrigins": ["http://www.your-domain-2.com"],
        "ExposeHeaders": []
    },
    {
        "AllowedHeaders": [],
        "AllowedMethods": ["GET"],
        "AllowedOrigins": ["*"],
        "ExposeHeaders": []
    }
]
</code></pre>
<ul>
<li><strong>AllowedOrigins</strong>: <mark>Specify the domains allowed to make requests</mark>.</li>
<li><strong>AllowedMethods</strong>: <mark>Define the permitted HTTP methods</mark>.</li>
<li><strong>AllowedHeaders</strong>: <mark>Specify the allowed headers</mark>.</li>
</ul>
<ul>
<li>Save Your Changes: Once you’ve added your CORS rules, save and you’re good to go!</li>
</ul>
]]></content>
        <published>2024-03-03T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[How to Read 1 Book in 1 Week]]></title>
        <id>https://feyzan.netlify.app/1-book-1-week</id>
        <link href="https://feyzan.netlify.app/1-book-1-week"/>
        <updated>2024-01-20T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/0*6ivejSKidFUiB8Ee" alt="" /></p>
<p>Reading is a way for us to <mark>discover new things</mark>. It is crucial as it helps <mark>expand our minds and enhance our imagination</mark>. Successful individuals like Bill Gates, Elon Musk, and Warren Buffet often indulge in reading books. On average, <mark>CEOs read 4–5 books in a month</mark>, totaling around 50 books a year for avid readers. After learning this fact, <mark>I became determined to read at least 1 book per week</mark>.</p>
<p>Reading a book is also considered a <mark>Keystone habit</mark>, an S-tier habit according to Improvement Pill, and I agree with this assessment. Keystone habits are incredibly powerful habits; once acquired, they <mark>pull other good habits into our lives</mark>. Successfully incorporating these habits guarantees drastic changes in our lives.</p>
<p>Examples of Keystone habits, besides reading books, include:</p>
<ul>
<li><mark>Exercise</mark></li>
<li><mark>Journaling</mark></li>
<li><mark>Meditation</mark></li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/0*r7H4KpdQbKVBvNJW" alt="captionless image" /></p>
<h2>How to read 1 book in 1 week:</h2>
<h3>1. Implementation Intention — <mark>Schedule your reading time</mark></h3>
<p>Usually, we buy a book with the intention of reading it, but in reality, it often remains untouched on the bookshelf.</p>
<p>To encourage reading, <mark>schedule dedicated time</mark> for it. Write down the schedule and the place where you plan to read. You can use a digital calendar like the one below:</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/0*KWsBcDuAAzK8zNeJ" alt="captionless image" /></p>
<h3>2. Chunking — <mark>Break down large tasks into smaller ones</mark></h3>
<p>We are often intimidated by the number of pages in a book, thinking it might take around 10 hours to finish a 1000-page book, discouraging us from starting. <mark>Break down the significant task into smaller ones</mark>, for example, aim to read one chapter a day. By chunking the task, you won’t feel overwhelmed, making it seem more manageable.</p>
<h3>3. <mark>Join a Book Club</mark></h3>
<p>Joining a book club can <mark>boost your enthusiasm for reading</mark>. Reading together with friends, engaging in discussions, and recommending books can significantly enhance the reading experience. <mark>These activities will increase your motivation</mark> to read more books.</p>
<p><mark>More enthusiasm = reading books faster/more frequently.</mark></p>
]]></content>
        <published>2024-01-20T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Onyx Boox Nova 3 Review: A Great E-Reader for Book Lovers]]></title>
        <id>https://feyzan.netlify.app/onyx-boox</id>
        <link href="https://feyzan.netlify.app/onyx-boox"/>
        <updated>2024-01-20T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<h1>Why buy an e-reader?</h1>
<p>Currently, I find myself reading digital books more often because my room is filled with physical books, and there’s no space for any more. Additionally, it’s inconvenient to carry multiple books while traveling. Because of these reasons, I decided to research e-readers, particularly Kindle and Onyx.</p>
<h1>Why not read on a phone?</h1>
<p>There are several reasons why I prefer not to use my phone for reading:</p>
<ol>
<li>Small screen size.</li>
<li>They are not dedicated to reading, leading to distractions (e.g., notifications).</li>
<li>Uncomfortable screen for prolonged reading (causing eye strain and fatigue).</li>
<li>Heavy to hold for extended periods.</li>
</ol>
<h1>Why choose Onyx?</h1>
<p>Initially, I was considering buying a Kindle, thinking it was the best e-reader available. However, <mark>Kindle only allows purchasing books from Amazon</mark>, and I often buy from Play Books, read on Gramedia Digital, or use Libby. In short, <mark>I needed an e-reader that supports the Play Store (Android OS-based)</mark>.</p>
<p>During a recent visit to my old office to meet my mentor, I had a conversation with the CEO of Radya Labs, Tito. As we discussed books and good reads, he introduced me to Onyx. <mark>Tito recommended the Onyx Boox Nova 3</mark>, which he uses for reading books on Play Books and note-taking. Trusting his judgment, I decided to explore Onyx further.</p>
<p>After returning from Radya Labs, I researched the Onyx Boox Nova 3. Convinced, I searched for it on e-commerce platforms. Unfortunately, the Onyx Boox Nova 3 was out of stock in Indonesia, and the ones available on e-commerce were priced between 8 to 10 million. <mark>I turned to Twitter and found a second-hand unit for 4.5 million IDR</mark>.</p>
<h2>Specifications</h2>
<ul>
<li>Display: <mark>E Ink Carta Plus, 7.8" touch</mark> (inductive Wacom + capacitive), resolution 1872x1404, 300 ppi, 16 shades of grey, with the SNOW Field function</li>
<li>Frontlight: MOON Light 2</li>
<li>Processor: 8-core, 1.8 GHz</li>
<li>RAM: 3 GB</li>
<li>Internal memory: 32 GB</li>
<li>Audio: Speaker, microphone</li>
<li>Wired interface: USB Type-C</li>
<li>Supported file formats: TXT, HTML, RTF, FB2, FB2.zip, DOC, DOCX, PRC, MOBI, CHM, EPUB, JPG, PNG, GIF, BMP, PDF, DjVu, MP3, WAV, CBR, CBZ</li>
<li>Wi-Fi: Wi-Fi IEEE 802.11 b/g/n/ac</li>
<li>Bluetooth: 5.0</li>
<li>Battery: Li-Pol, 3150 mAh</li>
<li>Operating system: Android 10.0</li>
<li>Size: 197.3 × 137 × 7.7 mm</li>
<li>Color: Black</li>
<li>Weight: 265 gr</li>
<li>Kit: ONYX BOOX NOVA 3 e-reader, User’s manual, Stylus, USB cable, Protective film, Warranty card</li>
<li>Warranty period: 1 year</li>
</ul>
<h2>Usage Review</h2>
<ul>
<li><mark>The 7.8-inch size is substantial yet lightweight</mark>, allowing easy one-handed use, and making it comfortable for reading.</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/0*jEN_wG8W5QLUSfTA" alt="" /></p>
<ul>
<li><mark>It supports the installation of various applications due to being Android-based</mark>. I have successfully installed Gramedia Digital, Libby, Manga PLUS, and Spotify.</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/0*3-BLjM2PcBadLsIX" alt="" /></p>
<ul>
<li>Reading on Gramedia Digital is enjoyable, especially for <mark>EPUB formats where you can customize the font and text size</mark>. However, for PDFs, the text may get cut off, and it’s challenging to read.</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1000/format:webp/0*-o3WQzgC5aB9EZiN" alt="" /><img src="https://miro.medium.com/v2/resize:fit:1000/format:webp/0*hBZ8oBsB3t-udm_n" alt="" /></p>
<p><img src="https://miro.medium.com/v2/resize:fit:1000/format:webp/0*6wem_DHs7hI3VLss" alt="" /><img src="https://miro.medium.com/v2/resize:fit:1000/format:webp/0*gny9Mn7U33op7VMa" alt="" /></p>
<ul>
<li>Reading on Play Books is also pleasant, although the refresh rate is not as smooth as a smartphone, causing some stutter when flipping pages.</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1000/format:webp/0*2sO40OVv389VftY7" alt="" /><img src="https://miro.medium.com/v2/resize:fit:1000/format:webp/0*gM7ljTXhe3qkLszc" alt="" /></p>
<ul>
<li><mark>Perfect for Manga enthusiasts!</mark> The size and display quality makes it perfect for reading Manga, providing an experience similar to reading on paper.</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1000/format:webp/0*2N2mSWmFgE96xXca" alt="" /><img src="https://miro.medium.com/v2/resize:fit:1000/format:webp/0*AA37aPoH9umOpkAc" alt="" /></p>
<ul>
<li><mark>Excellent for note-taking:</mark> The stylus is responsive, comfortable to use, and doesn’t require charging or complicated configurations to connect to the Onyx.</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/0*d6jJuOM4Eu9TSnTg" alt="" /></p>
<ul>
<li><mark>Long-lasting battery:</mark> When used solely for reading, the battery can last up to a week.</li>
<li><mark>Comfortable for the eyes:</mark> The screen closely resembles paper, is anti-glare, and doesn’t strain the eyes even during extended use.</li>
<li>Decent speakers: In addition to reading and note-taking, the speakers are adequate for listening to music, though not exceptional in quality.</li>
</ul>
<h2>Conclusion</h2>
<p>I am <mark>extremely satisfied with the Onyx Boox Nova 3</mark>. Its wide, lightweight design, suitability for note-taking, integrated speakers, and immersive reading experience without distractions make it an excellent choice, and it doesn’t strain the eyes. In my opinion, <mark>the Onyx Boox Nova 3 is a great fit for avid readers</mark>.</p>
]]></content>
        <published>2024-01-20T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Movie Review: Rear Window (10/10)]]></title>
        <id>https://feyzan.netlify.app/rear-window</id>
        <link href="https://feyzan.netlify.app/rear-window"/>
        <updated>2023-09-18T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p><img src="https://miro.medium.com/v2/resize:fit:1280/format:webp/0*wJhFHqVLCGxZUe0e.jpg" alt="" /></p>
<p>Wahai pembaca yang budiman, apakah Anda sudah menonton Rear Window-nya Hithcock? Jika belum, <em>do yourself a favor</em> dengan menonton film ini. <mark>Film ini menjadi film favoritku (urutan pertama, tentu saja) menggeser <strong>12 Angry Men</strong> dan <strong>It’s a Wonderful Life</strong></mark>.</p>
<p>Film ini bercerita tentang <mark>Jeff, fotografer terkenal dengan kaki yang patah dan terjebak di apartemennya</mark>. Dunianya terbatas hanya pada pemandangan dari jendela belakangnya. Dari kacamatanya, kita bisa melihat drama sehari-hari tetangganya, dan di tempat yang sangat biasa ini, simfoni manusia terjadi.</p>
<p>Jeff menonton tetangga-tetangganya menggunakan lensa kamera, yang tentu saja ini perlahan-lahan menjadi masalah etis. <mark><strong>Voyeurisme</strong> sebagai refleksi atas kondisi manusia</mark>. Hithcock menggambarkan ini seperti refleksi atas kondisi manusia, selalu memiliki rasa ingin tahu terhadap kehidupan orang lain. Dia juga mengajak kita untuk menatap ke dalam jurang keinginan kita, obsesi kita, dan kompleksitas moral kita.</p>
<p>Hitchcock menenun narasi ini dengan penuh ketegangan dan ketidaknyamanan. <mark>Suspens-nya sangat terasa</mark>, dan tentu saja ini bukan mekanika thriller sederhana. Ia menggali rasa cemas diri kita, menyoroti kerentanan kita akan hal yang tidak kita ketahui.</p>
<p>Film ini juga mengeksplorasi konsekuensi dari tindakan dan ketidaktindakan. Ketika Jeff mencurigai terjadi suatu kejahatan, ia dihadapkan pada <mark>dilema moral tentang apakah ia seharusnya turun tangan atau tidak</mark>. Ini menggugah pertanyaan tentang tanggung jawab kita ketika kita mengetahui potensi bahaya atau ketidakadilan.</p>
<p><mark>Isolasi fisik apartemen Jeff mencerminkan isolasi emosional dan psikologis</mark> yang sering kita alami dalam kehidupan perkotaan modern. Film ini menyoroti kebutuhan manusia akan hubungan sosial dan koneksi antarmanusia.</p>
<p>Dengan menggunakan lingkungan yang terbatas sebagai latar, <strong>Rear Window</strong> mengeksplorasi <mark>batas moral dan etika dalam berbagai situasi</mark>. Kapan seharusnya kita bertindak atas kecurigaan kita, dan kapan pengamatan kita dapat dianggap sebagai pelanggaran privasi atau pelanggaran etika? Ini adalah pertanyaan-pertanyaan yang akan membuat penonton merenung.</p>
<p>Penilaian: <mark>(10/10 bintang)</mark>.</p>
]]></content>
        <published>2023-09-18T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Automate Code Review dengan Amazon CodeGuru]]></title>
        <id>https://feyzan.netlify.app/codeguru</id>
        <link href="https://feyzan.netlify.app/codeguru"/>
        <updated>2022-11-12T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/0*dYLMVPBTxZ84okmD.png" alt="https://aws.amazon.com/" /></p>
<p>Amazon CodeGuru adalah sebuah <mark><em>code review automation service</em> dari AWS</mark> yang bisa membantu mengevaluasi <em>code</em> kita, dan <mark>merekomendasikan <em>code</em> terkait <em>performance</em> aplikasi</mark> kita. Amazon CodeGuru ini <mark>menggunakan machine-learning untuk mendeteksi <em>defect</em></mark> pada code. Untuk saat ini, <mark>hanya bahasa pemrograman Java saja yang di-support</mark>. Di tulisan ini kita akan mencoba mengasosiasikan AWS CodeCommit dengan Amazon CodeGuru.</p>
<p>Prerequisites: <mark>Silahkan buat code repository terlebih dahulu di Amazon CodeCommit</mark>.</p>
<p>Setelah code repository terbuat, <mark>buka halaman Amazon CodeGuru</mark>, dan buka sub-menu Repositories. Lalu klik <mark>Associate repository and run analysis</mark>.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*8kBylENPDr_dA11pKKHHtQ.png" alt="" /></p>
<p><mark>Pilih AWS CodeCommit sebagai source provider</mark>, dan pilih juga repository locationnya.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*YqBt4azEAx309cdR0UE7Jw.png" alt="" /></p>
<p>Lalu <mark>pilih branch yang nantinya akan di-scan</mark> oleh Amazon CodeGuru, lalu Asosiasikan.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*eDRFolMfboHkYcmvWITZmw.png" alt="" /></p>
<p>Sekarang, <mark>coba commit dan push code</mark> tersebut ke repo Amazon CodeCommit. Jika sudah, <mark>buat pull request</mark>.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*sszpfJMyuC6mFfYloqp3iw.png" alt="" /></p>
<p><img src="https://miro.medium.com/v2/resize:fit:2000/format:webp/1*bvWjT2RXD8e5Z3gKnOr7uQ.png" alt="" /></p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*GE8n9zk6cWUy1FPhLa7Z0g.png" alt="" /></p>
<p><img src="https://miro.medium.com/v2/resize:fit:2000/format:webp/1*36IgA0DYyGAmj5g6PVeyqw.png" alt="" /></p>
<p>Di tab <em>Changes</em>, Amazon CodeGuru akan <mark>memberi komen jika ada code yang bisa di-<em>fix</em> atau di-<em>improve</em></mark>.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*aCcWSfqpIhmCyHkJZznCiw.png" alt="" /></p>
]]></content>
        <published>2022-11-12T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Muslim Produktif — Bagian 1]]></title>
        <id>https://feyzan.netlify.app/muslim-produktif</id>
        <link href="https://feyzan.netlify.app/muslim-produktif"/>
        <updated>2022-10-23T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<blockquote>
<p>إِنَّ اللَّهَ عَزَّ وَجَلَّ يُحِبُّ إِذَا عَمِلَ أَحَدُكُمْ عَمَلًا أَنْ يُتْقِنَهُ</p>
</blockquote>
<blockquote>
<p><mark><em>Sesungguhnya Allah sangat mencintai orang yang jika melakukan suatu pekerjaan, dilakukan dengan itqan (tepat, terarah, jelas, tuntas).</em></mark> (HR. Thabrani).</p>
</blockquote>
<p>Sekarang ini saya sedang membaca buku berjudul <strong>The Productive Muslim</strong> karya Mohammed Faris. Menurut saya, <mark>semua muslim perlu membaca buku ini</mark>. Buku ini menjelaskan relevansi Islam di zaman pascamodern, cara memaksimalkan potensi, meraih kesuksesan dunia dan akhirat, serta menanamkan nilai-nilai Islam dalam keseharian. Jadi saya akan menerjemahkan, merangkum, dan menambahkan sebagian isi buku ke seri tulisan ini.</p>
<blockquote>
<p>إِنَّ اللهَ لا يُغَيِّرُ مَا بِقَوْمٍ حَتَّى يُغَيِّرُوا مَا بِأَنْفُسِهِمْ</p>
</blockquote>
<blockquote>
<p><mark>Sesungguhnya Allah tidak akan mengubah keadaan suatu kaum sehingga mereka mengubah keadaan yang ada pada diri mereka sendiri.</mark> (Surat Ar-Ra’d, ayat 11)</p>
</blockquote>
<h2>Apa itu Produktivitas?</h2>
<p>Produktivitas adalah <mark>Fokus × Energi × Waktu</mark> (untuk tujuan yang bermanfaat). Jika fokus, energi, dan waktu kita dihabiskan untuk hal yang tidak bermanfaat, maka itu tidak dapat disebut produktif.</p>
<ul>
<li><strong>Produktif beda dengan sibuk.</strong> Kita bisa saja sibuk seharian tetapi tetap tidak produktif. Contohnya, buang-buang waktu dalam meeting yang tidak memberi nilai tambah.</li>
<li><strong>Produktif itu tidak membosankan.</strong> Produktif bukan berarti harus menolak hiburan. <mark>Produktif berarti membuat pilihan yang tepat</mark>, tahu kapan harus bekerja keras, beristirahat, dan bersenang-senang.</li>
<li><strong>Kita tidak bisa selalu produktif.</strong> Konsisten menjaga produktivitas itu sulit. Ada hari ketika kita efektif, ada hari ketika tidak. Jangan khawatir, kita bukan robot. Fokuslah pada cara menjaga level produktivitas secara berkelanjutan.</li>
</ul>
<h2>Paradigma Islam tentang Produktivitas</h2>
<p>Islam membimbing kita meraih ketenangan dan kemakmuran hidup melalui sikap bertanggung jawab dan produktif. Berikut paradigma Islam tentang produktivitas.</p>
<h3>Produktivitas yang didorong oleh tujuan</h3>
<p>Jika kita memahami tujuan dan makna hidup, produktivitas akan meningkat secara signifikan.</p>
<blockquote>
<p>وَمَا خَلَقْتُ الْجِنَّ وَالْاِنْسَ اِلَّا لِيَعْبُدُوْنِ</p>
</blockquote>
<blockquote>
<p><mark>Aku tidak menciptakan jin dan manusia melainkan agar mereka beribadah kepada-Ku.</mark> (Surat Az-Zariyat, ayat 56)</p>
</blockquote>
<blockquote>
<p>وَاِذْ قَالَ رَبُّكَ لِلْمَلٰۤىِٕكَةِ ِانِّيْ جَاعِلٌ فِى الْاَرْضِ خَلِيْفَةً ۗ قَالُوْٓا اَتَجْعَلُ فِيْهَا مَنْ يُّفْسِدُ فِيْهَا وَيَسْفِكُ الدِّمَاۤءَۚ وَنَحْنُ نُسَبِّحُ بِحَمْدِكَ وَنُقَدِّسُ لَكَ ۗ قَالَ اِنِّيْٓ اَعْلَمُ مَا لَا تَعْلَمُوْنَ</p>
</blockquote>
<blockquote>
<p><mark>Dan (ingatlah) ketika Tuhanmu berfirman kepada para malaikat, “Aku hendak menjadikan khalifah di bumi.”</mark> Mereka berkata, “Apakah Engkau hendak menjadikan orang yang merusak dan menumpahkan darah di sana, sedangkan kami bertasbih memuji-Mu dan menyucikan nama-Mu?” Dia berfirman, “Sungguh, Aku mengetahui apa yang tidak kamu ketahui.” (Surat Al-Baqarah, ayat 30)</p>
</blockquote>
<p>Menurut dua ayat diatas, tujuan kita di dunia ini ada dua:</p>
<ul>
<li><mark>Menjadi Hamba Allah</mark></li>
</ul>
<p>Menerima bahwa kita hamba Allah berarti semua ucapan dan tindakan harus selaras dengan apa yang Allah cintai. <mark>Hidup kita akan lebih terarah</mark>, tidak terjebak dalam <em>rat race</em>. Kita menjalani hidup sesuai Al-Qur’an dan Sunnah. Jika tidak, kita mudah menjadi hamba harta, jabatan, atau hal duniawi lainnya.</p>
<ul>
<li><mark>Menjadi Khalifah di Bumi</mark></li>
</ul>
<blockquote>
<p>Setiap kalian adalah pemimpin, dan setiap kalian akan dimintai pertanggungjawabannya.
Seorang imam adalah pemimpin dan akan dimintai pertanggungjawabannya.
Seorang laki-laki adalah pemimpin atas keluarganya dan ia akan dimintai pertanggungjawabannya.
Seorang wanita adalah pemimpin atas rumah suaminya, dan ia pun akan dimintai pertanggungjawabannya.
Seorang budak juga pemimpin atas harta tuannya dan ia juga akan dimintai pertanggungjawabannya.
Sungguh setiap kalian adalah pemimpin dan setiap kalian akan dimintai pertanggungjawabannya. (HR. Bukhari: 4789)</p>
</blockquote>
<p>Banyak yang salah paham dengan makna tanggung jawab di sini. Kita sering mengira tanggung jawab berarti sekadar menjalankan tugas. Padahal, kata pemimpin di hadits ini menggunakan istilah “Roi” yang berarti penggembala.</p>
<p>Penggembala bukan hanya menjaga kawanan domba, tetapi juga memelihara, memastikan mereka berkembang, mencari padang rumput terbaik, dan merawat yang sakit. Artinya, <mark>peran pemimpin itu aktif dan proaktif</mark>. Kita tidak cukup menjalankan tugas, namun harus memastikan segala hal tumbuh dan menjadi lebih baik.</p>
<p>Contoh sederhana: orang tua mengira mengirim anak ke sekolah sudah cukup. Padahal, menjadi “Roi” berarti <mark>aktif memantau pendidikan anak</mark>—menanyakan apa yang dipelajari, tugas yang harus dikerjakan, dan seterusnya. Tugas kita adalah menjadi khalifah yang menjalankan peran “Roi” di bumi.</p>
<h3>Produktivitas yang didorong oleh nilai</h3>
<p>Nilai-nilai seperti <mark>amanah (dapat dipercaya), shiddiq (benar), ihsan (melakukan sebaik-baiknya), adil, rahmah (belas kasih), dan rifqu (lemah lembut)</mark> membantu kita memegang kompas moral dan menjaga martabat manusia. Inilah nilai yang wajib kita pegang.</p>
<p>Ada cerita tentang Khalifah Umar bin Abdul-Aziz. Ketika tamu datang membahas kepentingan Islam, beliau menyalakan lampu ruangannya. Namun saat pembicaraan berubah menjadi urusan personal, beliau mematikannya. Ketika ditanya alasannya, beliau menjawab bahwa lampu itu dibeli dari <mark>treasury Islam</mark>, sehingga hanya layak digunakan untuk urusan umat.</p>
<p>Selain itu, Rasulullah ﷺ mengingatkan empat pertanyaan yang akan kita hadapi di Hari Penimbangan:</p>
<ul>
<li><mark>Tentang ilmu pengetahuan</mark>, dan amal apa yang diperbuat darinya</li>
<li><mark>Tentang umur</mark>, dan bagaimana dihabiskan</li>
<li><mark>Tentang kehidupan</mark>, dan bagaimana dijalani</li>
<li><mark>Tentang harta</mark>: bagaimana cara mendapatkannya dan menggunakannya</li>
</ul>
<p>Empat hal ini terkait langsung dengan produktivitas. Apakah kita memanfaatkan ilmu secara produktif? Apakah usia kita diisi dengan kebaikan atau habis dalam hal sia-sia?</p>
<p>Jika kita fokus pada hari akhirat, kita terdorong untuk menjaga keseimbangan peran—bukan hanya sebagai pengusaha atau karyawan produktif, tetapi juga sebagai anak, orang tua, suami atau istri, guru, tetangga, warga, dan <mark>muslim produktif</mark>.</p>
]]></content>
        <published>2022-10-23T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Serve Files Lebih Cepat dengan CloudFront CDN]]></title>
        <id>https://feyzan.netlify.app/cloudfront</id>
        <link href="https://feyzan.netlify.app/cloudfront"/>
        <updated>2022-10-17T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p><img src="https://miro.medium.com/v2/resize:fit:1196/format:webp/0*szZE95ecnMgR17Rb.png" alt="docs.aws.amazon.com" /></p>
<p>Amazon CloudFront adalah sebuah layanan <mark>content delivery network (CDN)</mark>. Kita dapat mempercepat pengiriman file statis melalui protokol HTTP/HTTPS karena <mark>CloudFront secara otomatis memilih edge location terdekat</mark>. Layanan ini tersebar di seluruh dunia—2 di Australia, 2 di Amerika Selatan, 13 di Asia, 16 di Eropa, dan 20 di Amerika Serikat—serta dapat dipantau performanya melalui <mark>Amazon CloudWatch</mark>.</p>
<p>Di tulisan ini, kita akan:</p>
<ul>
<li>Membuat CloudFront distribution</li>
<li>Mengarahkan request pengguna ke domain distribution (bukan domain S3) agar <mark>CDN memilih edge location terdekat</mark></li>
</ul>
<h2>Prerequisites:</h2>
<ul>
<li>Siapkan file statis pada Amazon S3 bucket yang sudah <mark>ACLs enabled dan publicly accessible</mark>.</li>
</ul>
<ul>
<li>Pergi ke menu CloudFront, lalu klik <strong>Create distribution</strong>.</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:994/format:webp/1*8NGUK9v4NvMWCJ13n8lS_w.png" alt="" /><img src="https://miro.medium.com/v2/resize:fit:1008/format:webp/1*FMmyCphn5OY9voWpZFR7bQ.png" alt="" /></p>
<ul>
<li>Pada dropdown <strong>Origin domain</strong>, pilih S3 bucket yang sudah disiapkan, lalu klik <strong>Create control setting</strong>.</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*uPZcf-6juChe8VQITZqjfw.png" alt="" /></p>
<ul>
<li>Masukkan nama bucket, pastikan opsi <strong>Signing behavior</strong> diset ke <mark>Sign requests</mark>, lalu klik <strong>Create</strong>.</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*h-OUppbrTeB69tSxdMucuQ.png" alt="" /></p>
<ul>
<li>Di bagian <strong>Settings</strong>, pilih price class. Contohnya, gunakan <mark>“Use all edge locations (best performance)”</mark> untuk jangkauan maksimum. Jika ingin hemat biaya, pilih price class yang lebih terbatas.</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*Y8Hqvasmv81a6caM_CS9QQ.png" alt="" /></p>
<ul>
<li>Biarkan pengaturan lainnya default, kemudian klik <strong>Create distribution</strong>.</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*bgmETgbbxw9BvV3UspRk0Q.png" alt="" /></p>
<ul>
<li>Setelah distribution aktif, bandingkan kecepatan akses file langsung dari S3 dengan domain CloudFront. <mark>Gunakan domain distribution</mark> saat pengguna mengunduh file, dan amati perbedaannya—biasanya CloudFront memuat lebih cepat dibanding akses langsung ke S3.</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:2000/format:webp/1*dwtDAutJe2_ry93oBQBwdg.png" alt="" /></p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*PgPxnqpcuzrC097xNatqDg.png" alt="" /></p>
]]></content>
        <published>2022-10-17T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Transform Data dengan Amazon S3 Object Lambda]]></title>
        <id>https://feyzan.netlify.app/s3-object-lambda</id>
        <link href="https://feyzan.netlify.app/s3-object-lambda"/>
        <updated>2022-10-02T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/0*cqwnaAoALQMoKjKL.png" alt="aws.amazon.com" /></p>
<p>Amazon S3 digunakan untuk menyimpan data yang dapat dibagikan ke berbagai aplikasi. Namun, sebagian aplikasi membutuhkan <mark>format data yang spesifik</mark>. Kita bisa menyimpan data dalam banyak format, tetapi pendekatan tersebut tidak efisien. Alternatifnya, transform data sebelum dikonsumsi aplikasi.</p>
<p>Dengan <mark>Amazon S3 Object Lambda</mark>, kita dapat menjalankan Lambda function setiap kali aplikasi mengambil data. Fungsi tersebut dapat mengubah data ke format yang dibutuhkan, sehingga <mark>tak perlu menyimpan banyak versi data</mark> di S3.</p>
<p>Contoh penggunaan: aplikasi membutuhkan data JSON, sementara file di S3 masih berupa CSV.</p>
<h2>Prerequisite:</h2>
<ul>
<li>Buat Lambda function untuk convert CSV ke JSON. Contoh kodenya tersedia di repo ini: <mark><a href="https://github.com/fauzanjantung/lambda-csv-to-json/blob/main/index.py">lambda-csv-to-json/index.py</a></mark></li>
</ul>
<p>Untuk memulai, buka S3 terlebih dahulu.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*3OC2mbuEGoFy6H8b1qbxqw.png" alt="" /></p>
<p>Lalu buat bucket yang publicly accessible, dan upload data ke bucket tersebut. Contoh dataset CSV yang akan digunakan:</p>
<p>Username,Login email,Identifier,First name,Last name
<a href="mailto:booker12,rachel@example.com">booker12,rachel@example.com</a>,9012,Rachel,Booker
<a href="mailto:grey07,laura@example.com">grey07,laura@example.com</a>,2070,Laura,Grey
<a href="mailto:johnson81,craig@example.com">johnson81,craig@example.com</a>,4081,Craig,Johnson
<a href="mailto:jenkins46,mary@example.com">jenkins46,mary@example.com</a>,9346,Mary,Jenkins
<a href="mailto:smith79,jamie@example.com">smith79,jamie@example.com</a>,5079,Jamie,Smith</p>
<p>Berikut langkah membuat pipeline transformasi dengan Object Lambda:</p>
<ul>
<li>Buka tab <strong>Access Points</strong>, kemudian klik <strong>Create access point</strong>.</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*h5HK9SIhgQoSQ82qWWllTg.png" alt="" /></p>
<ul>
<li>Isi nama access point, pilih <strong>Network Origin</strong>. Di contoh ini gunakan Internet Network Origin dan hilangkan centang “Block all public access” agar bisa diakses publik. Setelah selesai, klik <strong>Create access point</strong>.</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*kYz-k8L0uBIJ-d2D8gD7Ng.png" alt="" /><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*ISszmpj0YMBDfvNHwsKhvA.png" alt="" /></p>
<ul>
<li>Buka menu <strong>Object Lambda Access Points</strong> di sidebar, lalu pilih <strong>Create Object Lambda Access Point</strong>.</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:366/format:webp/1*mQP41q9R_eccmHTOcC3lKA.png" alt="" /><img src="https://miro.medium.com/v2/resize:fit:1636/format:webp/1*no26TBxxb4Rse7v1MiIHpQ.png" alt="" /></p>
<ul>
<li>Isi nama Object Lambda Access Point, pilih region, kemudian pilih Access Point yang baru dibuat melalui <strong>Browse S3</strong>. Selanjutnya, pilih Lambda function <mark>CsvToJsonConverter</mark> yang sudah disiapkan.</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1152/format:webp/1*L6pfXpCXojxeJjXpE6OLeQ.png" alt="" /><img src="https://miro.medium.com/v2/resize:fit:850/format:webp/1*kSXQ0OX66i8vJCPh_SSsNg.png" alt="" /></p>
<ul>
<li>Klik <strong>Create Object Lambda Access Point</strong>.</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*SwZ38I115R7rQV6CQgwYxQ.png" alt="" /></p>
<ul>
<li>Buka detail Object Lambda Access Point dan salin ARN-nya.</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:1188/format:webp/1*dd2ShDYs7lkZR8XI26MRew.png" alt="" /><img src="https://miro.medium.com/v2/resize:fit:814/format:webp/1*srIbkO0hDE43WwgeOUH_DQ.png" alt="" /></p>
<ul>
<li>Terakhir, buat satu Lambda function lagi untuk mengambil data yang sudah dikonversi. Contoh kode:</li>
</ul>
<pre><code class="language-python">import boto3
s3 = boto3.client('s3')

def lambda_handler(event, context):
    response = s3.get_object(
        Bucket='{OBJECT-LAMBDA-ACCESS-POINT}',
        Key='dev-team.csv')
    
    return response['Body'].read()
</code></pre>
<p>Ganti <code>{OBJECT-LAMBDA-ACCESS-POINT}</code> dengan ARN yang telah disalin. Setelah membuat fungsi, konfigurasi test event, lalu jalankan pengujian. Lambda tersebut akan mengembalikan respons JSON—hasil konversi file CSV di bucket S3—sedangkan file asli tetap utuh.
Key= ‘dev-team.csv’</p>
<blockquote>
<p>return response[‘Body’].read()</p>
</blockquote>
<p>Ganti <code>{OBJECT-LAMBDA-ACCESS-POINT}</code> dengan ARN yang telah disalin. Setelah membuat fungsi, konfigurasi test event, lalu jalankan pengujian. Lambda tersebut akan mengembalikan respons JSON—hasil konversi file CSV di bucket S3—sedangkan file asli tetap utuh.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1276/format:webp/1*v7Lv3E58gaUcQLxFOun_kA.png" alt="" /><img src="https://miro.medium.com/v2/resize:fit:726/format:webp/1*JhOQeEymKH1hlzuYJ7kd_w.png" alt="" /></p>
]]></content>
        <published>2022-10-02T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Tentang AWS Elastic Beanstalk]]></title>
        <id>https://feyzan.netlify.app/beanstalk</id>
        <link href="https://feyzan.netlify.app/beanstalk"/>
        <updated>2022-07-17T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*_UfUT1FVrqIgXolwIZ_DLw.png" alt="Generated by AI" /></p>
<p>AWS Elastic Beanstalk adalah sebuah service yang memungkinkan kita untuk <mark>mengupload code web app beserta konfigurasi environment</mark>. Elastic Beanstalk kemudian akan secara otomatis menyediakan resources yang diperlukan—seperti EC2, Elastic Load Balancing, automatic scaling, dan lain-lain—untuk menjalankan aplikasi tersebut.</p>
<p>Service ini memudahkan developer yang belum familiar dengan AWS untuk <mark>deploy, scale, dan memonitor web app</mark>. Elastic Beanstalk sangat fleksibel karena mendukung berbagai platform dan bahasa pemrograman seperti Ruby, PHP, Node.js, Python, .NET (Windows), Go, Java, Docker, dan lainnya.</p>
<p>Di AWS Elastic Beanstalk ada 2 environment tier:</p>
<h3>Web server environment</h3>
<p>Environment ini biasanya digunakan untuk aplikasi web pada umumnya yang berjalan di HTTP port 80. Tier ini menyediakan beberapa resources penting:</p>
<ul>
<li><mark>Amazon Route 53</mark>: URL yang dibuat Route 53 akan diasosiasikan dengan Elastic Load Balancer.</li>
<li><mark>Elastic Load Balancer</mark>: Terintegrasi dengan auto-scaling group untuk menyesuaikan kapasitas aplikasi sesuai traffic.</li>
<li><mark>EC2 Instances</mark>: Elastic Beanstalk minimal menyediakan satu instance untuk menjalankan aplikasi.</li>
</ul>
<h3>Worker environment</h3>
<p>Environment ini cocok untuk aplikasi dengan backend processing task yang berinteraksi dengan AWS SQS. Di tier ini akan terpasang daemon pada setiap EC2 instance untuk <mark>menarik request dari SQS queue</mark>.</p>
<h2>Deployment Options</h2>
<p>Elastic Beanstalk menyediakan beberapa deployment option untuk memudahkan pengelolaan infrastruktur: <mark>All at once</mark>, <mark>Rolling</mark>, <mark>Rolling with additional batch</mark>, dan <mark>Immutable</mark>.</p>
<h3>All at once</h3>
<p>Ini adalah deployment option default. Opsi ini akan merilis aplikasi ke semua resources secara bersamaan sehingga <mark>berpotensi menimbulkan downtime</mark> bagi user.</p>
<p>Ada jawaban bagus dan mudah dimengerti di stackoverflow tentang opsi deployment EB, bisa dilihat disini <a href="https://stackoverflow.com/questions/38656595/difference-between-rolling-rolling-with-additional-batch-and-immutable-deployme">https://stackoverflow.com/questions/38656595/difference-between-rolling-rolling-with-additional-batch-and-immutable-deployme</a>.</p>
<blockquote>
<p>All at once: Replace all <code>v1</code> with <code>v2</code> at the same time. Failure not handled.</p>
</blockquote>
<h3>Rolling</h3>
<p>Rolling dapat meminimalkan disruption dibanding all at once. Elastic Beanstalk akan melakukan deployment dalam beberapa batch, mengupdate sebagian resources setiap kali sehingga kita mungkin memiliki dua versi aplikasi dalam waktu singkat. <mark>Aplikasi tetap bisa melayani request</mark> selama proses berlangsung.</p>
<blockquote>
<p>Rolling: Replace <code>v1</code> instances with <code>v2</code> instances one at a time. Watch for failures.</p>
</blockquote>
<h3>Rolling with additional batch</h3>
<p>Prinsipnya mirip rolling, tetapi opsi ini menambahkan batch ekstra sehingga <mark>kapasitas tersedia tetap stabil selama update</mark> berlangsung.</p>
<blockquote>
<p>Rolling with batch: Create some new <code>v2</code> instances. If successful, roll out on <code>v1</code> instances. When all are <code>v2</code> instances, scale back to original size.</p>
</blockquote>
<h3>Immutable</h3>
<p>Opsi ini membuat instance baru sebanyak resource yang ada sehingga ukuran environment sementara menjadi dua kali lipat. Setelah instance baru lolos health check, environment lama akan dihapus untuk <mark>deployment yang aman dan tanpa downtime</mark>.</p>
<blockquote>
<p>Immutable: Don’t change <code>v1</code> instances. Create same number of <code>v2</code> instances. Wait for success, then stop <code>v1</code> instances.</p>
</blockquote>
<h3>Demonstrasi</h3>
<p>Untuk pergi ke halaman AWS Elastic Beanstalk, kita bisa search Elastic Beanstalk di kolom pencarian di AWS Console lalu klik menu Elastic Beanstalk. Untuk deploy di Elastic Beanstalk, kita bisa mulai dengan klik button Create Application.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*0wMg-OfS_YJSX1qCIKmqcg.png" alt="captionless image" /></p>
<p>Kita bisa memberi nama aplikasi serta menambahkan tag, misalnya key “env” dengan value “test” untuk <mark>membantu pengelompokan resource</mark>.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*i_j1U-rS8ve6wj00x42aHg.png" alt="captionless image" /></p>
<p>Pilih platform aplikasi—contoh demo ini menggunakan PHP di Amazon Linux 2. Untuk application code, kita bisa upload dari lokal ataupun S3; pada demo ini digunakan <mark>sample application bawaan</mark>.</p>
<p>Kita bisa langsung buat aplikasi dengan default setting, tapi aku akan coba menjelaskan custom configuration. Klik configure more options untuk pergi ke menu configurations.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*zH9Qa88q1swLDkPdrQhXMg.png" alt="captionless image" /></p>
<p>Di Presets tersedia opsi Low cost, High availability, dan Custom. Low cost akan menggunakan resource minimal, sementara High availability menerapkan deployment ke beberapa Availability Zone dengan <mark>auto-scaling dan load balancing aktif</mark>.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*Tdj1btF78b_uSP29MqTV1w.png" alt="captionless image" /></p>
<p>Kita juga bisa mengatur instance agar meng-upload rotated logs ke S3. Di bagian ini kita dapat mengaktifkan log rotation dan mengatur <mark>lama retensi log</mark>, misalnya hingga 10 tahun.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*I-vFkm1Mo0QgNjf6Ljjm8A.png" alt="captionless image" /></p>
<p>Untuk instances, t2.micro akan menjadi instance default kita, yang mana cukup oke. Kita juga bisa atur konfigurasi root volume instance ini.</p>
<p>Pada auto-scaling, tentukan jumlah minimum dan maksimum instance, pilih Availability Zone, serta atur trigger. Misalnya, set upper threshold CPU utilization 75% dan lower threshold 30%. Jika CPU melampaui 75% selama lima menit, <mark>auto-scaling akan otomatis menambah instance</mark>.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*7q3ncOvr83EystlBwkJYug.png" alt="captionless image" /><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*r3SuLCgeXbfYHR_XAnTzYg.png" alt="captionless image" /></p>
<p>Lalu ada notifikasi, kita bisa input email kita untuk mendapatkan updates terkait event yang terjadi di environment kita. Kita bisa menggunakan SNS. SNS ini akan mengirim email ke kita tiap ada event penting.</p>
<p>Kita juga bisa menambahkan database ke EB kita dengan cara restore existing snapshot atau setup database baru.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*8d-r4nA_cZ57LGloCSeYJw.png" alt="captionless image" /></p>
<p>Kalau kita klik save, Elastic Beanstalk akan membuat environment kita sesuai dengan konfigurasi kita. Ini akan memakan waktu selama beberapa menit untuk menyiapkan dan memproses semua resources yang dibutu</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*kHmhzh1Vtihby3YrEcBKmQ.png" alt="captionless image" /></p>
<p>Berhasil! Kita telah berhasil deploy aplikasi php ke Elastic Beanstalk. Kita bisa langsung membuka web app tersebut dengan klik link yang tersedia di detail environments.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*PV-FkhPHn2qL6EKQnt8FQw.png" alt="captionless image" /></p>
<p>AWS EB juga menyediakan logs, kita bisa mendownload logs dari EC2 Instance di menu logs. Kita juga bisa melihat total requests, CPU Utilization di menu Monitoring, juga kita bisa melihat semua event yang terjadi di menu Events.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*pogsEGrsmn8GhpQQfLBlJA.png" alt="captionless image" /><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*pt5gRlVWEKmYirKE5qTWiQ.png" alt="captionless image" /><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*nM9hClEVOdtyYU2VvPmnyA.png" alt="captionless image" /><img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*pwTdFmj80KsJzYeYNP56ew.png" alt="captionless image" /></p>
<p>Semuanya sangat simpel dan memudahkan ya. Gimana? Tertarik untuk coba AWS Elastic Beanstalk untuk web app kamu yang selanjutnya?</p>
]]></content>
        <published>2022-07-17T00:00:00.000Z</published>
    </entry>
</feed>