Skip to content

podcast

Effective Leadership in a Technical Role

Welcome to "Continuous Improvement," the podcast where we discuss strategies, tips, and insights to help you excel as a leader in the tech industry. I'm your host, Victor, a seasoned software engineer turned leader, and in today's episode, we'll be diving into a topic that is essential for anyone looking to transition into a leadership role – "Developing Leadership Skills in the Tech Industry."

As a software engineer, you are accustomed to focusing on completing tasks and ensuring technical excellence. But as you progress in your career, you'll realize that technical expertise alone is not enough to succeed in the tech industry. You'll need to navigate through a variety of issues and crises on a daily basis. The good news is, with an open mind and continuous learning, you can develop the skills required to lead a team effectively.

To begin with, it's crucial to understand the fundamental principles of leadership. As you climb the ladder, your responsibilities will shift from completing tasks to providing direction, guiding others, and considering overarching corporate strategies. That's what the title "Director" signifies – someone who knows the destination and leads the team towards it. So, my advice is to stay curious, learn about your company's decision-making processes, and always be open to asking questions and seeking knowledge.

Leadership doesn't always mean being overbearing or extremely extroverted. In fact, there are six different leadership styles that cater to different situations. Let's take a quick look at them:

  1. Coercive Leadership, effective during crises.
  2. Authoritative Leadership, inspiring others through vision.
  3. Affiliative Leadership, focusing on team unity.
  4. Democratic Leadership, building consensus.
  5. Coaching Leadership, aiming for individual growth.
  6. Pacesetting Leadership, setting performance standards.

It's important to understand your own leadership style and adapt it to suit different scenarios and team dynamics. There is no one-size-fits-all approach. Your style may need to change as the team progresses and faces various challenges.

To be an effective leader, you must know both yourself and your team members. Each individual has unique strengths and weaknesses, and it's your role to guide the team towards achieving its goals. This involves setting objectives, fostering teamwork, communicating clearly, and collaborating with stakeholders. Fairness is crucial when resolving conflicts within the team. Recognize and reward positive contributions while addressing any negative impacts. And above all, lead by example – your actions speak louder than words, making you a role model for the team.

Managing a team requires not only competence but also emotional intelligence. Understanding each team member's needs, work styles, relationships, and conflicts is essential. Formal and respectful communication fosters an environment of mutual respect and cooperation.

Thriving as a leader in the tech industry requires more than just technical skills. It involves envisioning the future, showing determination, displaying integrity and commitment, exuding confidence, and communicating effectively. Additionally, you should demonstrate creativity, inspire your team, foster collaboration, and be authentic in your leadership approach.

I hope you find the insights and strategies discussed in this episode helpful as you develop the self-awareness required to become an effective leader. Remember, you don't have to wait for permission to step up – take the initiative when urgent issues arise. Be proactive in seeking the resources you need because, in a world with a shortage of effective leaders, you have the opportunity to make a significant impact.

Thank you for joining me today on "Continuous Improvement." If you found value in this episode, be sure to subscribe to our podcast for more insightful discussions on leadership in the tech industry. Until next time, keep improving and leading with confidence!

The Importance of Communication as a Software Developer

Hello everyone, and welcome to "Continuous Improvement," the podcast that's all about personal and professional growth. I'm your host, Victor, and today we're going to dive into a topic that may seem obvious but is often underestimated—why software developers need to be good communicators.

As a team leader in the software development industry, I've learned firsthand the impact that communication skills can have on one's career. It's not just about writing code or solving technical challenges; being able to effectively communicate ideas, progress, and feedback is crucial in our field.

One area where good communication skills are essential is in conducting a good demo. Regardless of what you're building—a website, an app, or even a physical product—a well-executed demo can improve cross-team collaboration, build trust with stakeholders, and boost team morale. It's a chance to showcase your work and gather valuable feedback.

Working in cross-functional teams is another situation where strong communication skills are critical. Collaboration between engineers, designers, and client product owners is essential for delivering value quickly and meeting the demands of our dynamic industries.

So, how can we improve our communication skills when it comes to demos and working in teams? Let's explore some tips from the blog post.

One of the key takeaways is the importance of providing context during a demo. Zooming out and setting the scene helps the audience understand the user benefits and the purpose behind the product or feature you're presenting.

Another valuable tip is to tell a story during your demo. Sharing the challenges you've overcome and the lessons you've learned keeps the audience engaged and shows your problem-solving abilities.

Speaking at a pace that the audience can follow and avoiding jargon is crucial. Going slow and keeping it simple ensures that everyone understands what you're presenting, regardless of their technical background.

Preparation is also key. Rehearse your demo and have everything set up in advance to maintain audience engagement and avoid any technical hiccups.

Beyond just the technical aspects, good communication skills during demos and in cross-functional teams can lead to multiple benefits. They help build better products through quick feedback loops, improve team morale by encouraging discussions and resolutions, and strengthen stakeholder relationships by engaging them in the development process.

Lastly, developing your presentation skills and interacting more closely with stakeholders are additional advantages of regularly conducting demos. It's an opportunity for personal growth and honing your abilities, especially for those in the early stages of their careers.

Remember, good communication is not just about public speaking or demos; it's about engaging in meaningful conversations, sharing valuable perspectives, and building positive relationships with others.

I hope the insights from today's episode have highlighted the importance of communication in our field and provided you with some valuable tips for improving your own skills. Speak with intention, slow down, and breathe. I'm sure you'll see the positive impact it can have on your career.

That wraps up this episode of "Continuous Improvement." Thank you for joining me today. If you have any thoughts or questions, I'd love to hear from you. You can reach out to me through our podcast website. Until next time, keep striving for improvement in all that you do.

Setting Up Auto-Formatting for Python in Visual Studio Code

Welcome back to another episode of Continuous Improvement! I'm your host, Victor. Today, we'll be talking about a common problem many Python developers face – formatting issues. We'll explore how integrating a code formatter into your editor can make your life easier. So, let's dive in!

Have you ever found yourself struggling with formatting problems while writing Python code? They can be quite troublesome, especially during code reviews and when using automated tools that detect such issues. But fear not, because I have a solution for you!

The first step is to install Google's yapf formatter. Simply open your command line or terminal and type in:

pip install yapf

Once you have yapf installed, it's time to configure your code editor – in this case, we'll be focusing on Visual Studio Code. Open VS Code and press "Command + Shift + P" if you're on a Mac, or "Ctrl + Shift + P" if you're on Linux. In the search bar, type "Open settings (JSON)" and add this line:

"python.formatting.provider": "yapf"

This step tells VS Code to use yapf as the Python code formatting provider. But we can take it a step further! If you want your code to automatically format upon saving, rather than just receiving tips within the editor, add this line as well:

"editor.formatOnSave": true

By enabling this setting, each time you save your Python file, it will be automatically formatted. This can save you a lot of time and effort.

Now, here's an optional step. If you wish to use your project's .style.yapf file instead of the global styles, add the following line to your VS Code settings:

"python.formatting.yapfArgs": ["--style", ".style.yapf"]

Including this setting allows you to customize the formatting rules according to your project's requirements. It provides even more flexibility when it comes to code formatting!

And that's it! With yapf installed and configured in Visual Studio Code, you can now enjoy the benefits of automatic code formatting. Just imagine, no more worries about missing new lines or incorrect indentation – the formatter will take care of it for you!

I encourage you to give it a try. Test the auto-formatting feature by intentionally leaving out a new line at the end of your Python file. When you save it, yapf will automatically correct this issue for you.

That brings us to the end of today's episode. I hope you found this information helpful in improving your workflow as a Python developer. Remember, continuous improvement is key to becoming better at what we do.

Thanks for tuning in to Continuous Improvement! I'm Victor, your host. Stay curious, stay dedicated, and keep striving for excellence. Until next time!

How to Install Ubuntu Desktop on an AWS Instance Using Chrome Remote Desktop

Hello everyone and welcome back to another episode of Continuous Improvement. I'm your host, Victor, and today we're going to talk about an innovative way to access your desktop environment from anywhere using the power of the cloud.

In today's cloud-centric world, we've moved many resources to the cloud - photos, files, and even servers. So why not your desktop environment? Imagine not needing to carry a heavy laptop and being able to access your computing power from any thin client, such as a tablet with a keyboard, from anywhere. Well, today I'm going to introduce you to a solution that combines AWS, Ubuntu Desktop, and Chrome Remote Desktop to achieve just that.

Now, there are other solutions available, like AWS Workspace or VNC connection, but Chrome Remote Desktop offers the lowest latency and performance closest to a native desktop. So let's dive into the steps to set it up.

Step one, log in to the AWS console and launch an instance using the Ubuntu Server AMI. This will be the foundation of our remote desktop environment.

Once the instance is launched, we need to SSH into the Ubuntu server. Update the package manager and install wget, which we'll need for the next step.

In step three, we download the Chrome Remote Desktop package and install it on our Ubuntu server. This will allow us to connect to the desktop environment remotely.

Now, to make the desktop environment more user-friendly, in step four, we install the Xfce desktop GUI environment. This distribution has been found to perform best, especially over slow networks.

Step five is configuring Chrome Remote Desktop to use Xfce by default. This ensures a seamless experience when connecting remotely.

In step six, we install xscreensaver as an alternative to the default Xfce locker, which is not compatible with remote desktop.

Step seven is to disable the display manager, as there is no display connected to our AWS instance.

In step eight, we add our user account to the Linux group and then log out. This ensures the user has the necessary permissions and settings for remote desktop access.

Now that we have prepared our Ubuntu server, in step nine, we switch to our local laptop browser. We navigate to Chrome Remote Desktop's Headless Mode and follow the steps to set up another computer. This will generate a command that we need to copy into our AWS instance.

And finally, once everything is set up, in step ten, we can remotely connect to our Ubuntu desktop environment using Chrome Remote Desktop. Now you can access your desktop from anywhere, using any device.

That's it for today's episode of Continuous Improvement. I hope you found this innovative solution for accessing your desktop environment remotely helpful. Whether you're a software engineer or anyone who wants access to a full set of development tools at all times, this setup can be a game-changer.

As always, if you have any questions or want to share your own continuous improvement experiences, feel free to reach out to me on our website or social media. Until next time, keep improving and embracing innovation to achieve your goals.

How to Install Ubuntu on an External Drive Using macOS

Hello and welcome back to another episode of Continuous Improvement. I'm your host, Victor, and today we're going to talk about a unique topic - how to install Ubuntu on an external hard drive without risking the removal of your macOS installation.

Now, we know that many of you may be using Mac hardware but also want to experience the wonders of Ubuntu. So, let's dive into the step-by-step process of achieving this without messing up your bootloader.

The first step is to plug in your external SSD or HDD and open Disk Utility. Format the drive to MS-DOS (FAT) to ensure compatibility.

Next, open a Terminal window and run the command diskutil list. This will help you find the IDENTIFIER of your external hard drive, such as disk2 in our example.

Now that we have identified the external drive, it's time to download and install VirtualBox from the official website. VirtualBox is a powerful tool that allows us to create virtual machines on our Mac.

After the installation, run two commands in the Terminal window to create a virtual machine using your external drive as the raw disk. Type in sudo VBoxManage internalcommands createrawvmdk -filename bootcamp.vmdk -rawdisk /dev/disk2 and then sudo /Applications/VirtualBox.app/Contents/MacOS/VirtualBox. This will start VirtualBox with admin rights.

Now, in VirtualBox, click on "New" and select "Expert Mode". Choose the option "Use an existing virtual hard disk file" and browse for the bootcamp.vmdk file that we created earlier.

In the "System" tab, make sure to check the box for "Enable EFI (special OSes only)" to ensure compatibility with macOS.

Moving on to the "Storage" tab, we need to mount the Ubuntu ISO file to the virtual machine. You can download the Ubuntu ISO file from the official Ubuntu website.

Great! Now it's time to start the virtual machine and proceed with the Ubuntu installation. Follow the on-screen instructions and be patient as the installation process completes.

Once the installation is complete, you'll be ready to boot from the external drive. But before that, there's one more thing. For Mac users, you'll need to reduce the Security level and allow booting from external media.

To do this, restart your Mac and press and hold Command-R immediately after you see the Apple logo to start macOS recovery. From there, navigate to the Security options and allow booting from external media.

Fantastic! You're almost there. Restart your Mac again, but this time, press and hold the Option key. You'll now see a menu where you can choose your drive with the EFI label. That's your Ubuntu installation on the external drive.

And there you have it! You can now enjoy the wonders of Ubuntu on your Apple hardware without compromising your macOS installation. Just keep in mind that you might need to sort out some driver issues, but don't worry, the process we've discussed today will make it much easier for you.

That concludes today's episode of Continuous Improvement. I hope you found this guide helpful and that it encourages you to explore new possibilities in the world of technology. Remember, with continuous improvement, we can achieve great things.

Until next time, I'm Victor, signing off. Take care and stay curious!

How to Fix the "Access Denied" Error in an AWS Amplify Angular App

Welcome back to Continuous Improvement, the podcast where we explore tips and tricks for enhancing your software development experience. I'm your host, Victor. In today's episode, we'll be discussing a common issue encountered when deploying an Angular app on AWS Amplify.

Have you ever deployed an Angular app on AWS Amplify and faced an "Access Denied" error when trying to access a defined path in your router? Well, fear not, because I have a solution for you.

The first step to resolving this issue is navigating to the AWS Console. Once you're there, select "Rewrites and Redirects" in your Amplify app settings.

Now, in order to allow access to the defined path, we'll need to add a new rewrite and redirect rule. Click on "Open Text Editor."

Here comes the crucial part: inserting the correct rewrite and redirect rule. Don't worry, I'll provide you with the necessary code.

In the text editor, paste the following code:

[
    {
        "source": "</^[^.]+$|\\.(?!(css|gif|ico|jpg|js|png|txt|svg|woff|ttf)$)([^.]+$)/>",
        "target": "/index.html",
        "status": "200",
        "condition": null
    }
]

This code snippet will ensure that any request to your defined path is redirected to the index.html file, allowing access without encountering the "Access Denied" error.

After you've added this rule, give it a try by accessing your URL again. Voila! It should now work as expected, and you can navigate to your defined path without any issues.

And that's it for today's episode of Continuous Improvement. Remember, don't let deployment challenges hinder your development progress. Stay tuned for more valuable tips and tricks to enhance your software development journey.

Thank you for listening to Continuous Improvement. I'm your host, Victor, and until next time, keep improving!

Writing Your Android App to Run in Background Mode as a Service

Welcome back to another episode of Continuous Improvement, the podcast where we explore tips and strategies to enhance your app development skills. I'm your host, Victor. In today's episode, we're going to talk about a common issue that many developers face – maintaining app functionality in the background. Have you ever experienced your app losing its functionality when users switch to other apps or lock their screens? It can be quite frustrating, right? Well, worry no more because we have some solutions for you!

But before we dive into that, let's quickly talk about the Android app Activity lifecycle. When a user switches away from your app, the activity is terminated, and the onDestroy() method is triggered. While this behavior is necessary for memory management and battery life, it can cause your app's states and functionalities to be lost.

To overcome this challenge, we recommend refactoring your app by separating background functionalities into a service. A service is an application component capable of performing long-running operations in the background, independent of the user interface.

Let me give you an example of how to create a service in Kotlin:

import android.app.Service

class MyService : Service() {

    override fun onBind(intent: Intent): IBinder? {
        return null
    }

    override fun onCreate() {
      // Place your service logic here
    }

    override fun onDestroy() {
      // Clean up your service logic here
    }

}

Once you've created your service, remember to define it in your AndroidManifest.xml file to ensure it is recognized by the system:

<application>
    ...
    <service android:enabled="true" android:name="com.victorleungtw.myapp.services.MyService"></service>
</application>

Now, if you want to start the service in your activity, simply add this line of code:

startService(Intent(this, MyService::class.java))

And, to stop the service, include this line:

stopService(Intent(this, MyService::class.java))

These changes will improve the structure of your app, but your app still won't be able to run indefinitely in the background. To achieve that, you need to add two more methods within your service. Here's an example:

class MyService : Service() {
    // ... existing code

    @RequiresApi(Build.VERSION_CODES.O)
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val channelId =
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    createNotificationChannel("my_service", "My Background Service")
                } else {
                    // If it's an earlier version, the channel ID is not used
                    ""
                }

        val notification: Notification = Notification.Builder(this, channelId)
                .setContentTitle("Title")
                .setContentText("Text")
                .build()
        startForeground(2001, notification)

        return START_STICKY
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private fun createNotificationChannel(channelId: String, channelName: String): String {
        val channel = NotificationChannel(channelId,
                channelName, NotificationManager.IMPORTANCE_NONE)
        channel.lightColor = Color.BLUE
        channel.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
        val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        service.createNotificationChannel(channel)
        return channelId
    }
}

By creating a notification, you can inform users that your app continues to run in the background. This notification can be customized to match your app's branding and provide relevant information.

With these changes, your app can now run seamlessly in the background, maintaining functionalities like Bluetooth connectivity, data syncing, or playing music.

And that brings us to the end of today's episode of Continuous Improvement. I hope you found these tips helpful in enhancing your app's functionality in the background. Remember, a seamless user experience is crucial for app success. If you have any questions or suggestions for future episodes, feel free to reach out to me. Until then, keep coding and keep improving!

Caveats with Android In-App Browsers

Welcome back to another episode of Continuous Improvement, the podcast dedicated to helping developers troubleshoot and improve their web applications. I'm your host, Victor, and today we're going to discuss a common browser compatibility issue that can cause headaches during development.

Picture this scenario: you've developed a web application, thoroughly tested it on different browsers, and deployed it to production. But unexpectedly, you encounter a problem specifically with Android in-app browsers that was not caught during development. Thousands of failed transactions start pouring in, leaving you scratching your head.

In today's episode, I want to share my own experience with this issue, hoping to save you valuable troubleshooting time in the future.

The problem lies in the webView of third-party Android in-app browsers, which you have no control over. If the setJavaScriptEnabled method is set to false, you'll find yourself at a dead-end. But even if the frontend code still manages to load, you might run into another obstacle - the setDomStorageEnabled setting is false by default.

To shed some light on this, let's head over to the official Android documentation. According to the documentation for the setDomStorageEnabled method, this boolean flag determines whether the DOM storage API is enabled or not. By default, it's set to false, effectively disabling the DOM storage API. And here's where the trouble begins if your code relies on accessing the localStorage object.

The localStorage object is widely used in web applications to store data locally, but if it's not available due to the disabled DOM storage API, your code execution can come to a screeching halt. The problem is, this issue doesn't produce a clear error message, making troubleshooting particularly challenging, especially within the Android in-app browser environment.

Fortunately, there's a simple solution. You can add a condition to check if localStorage is available before proceeding with your code. By doing this, you can handle the situation gracefully and avoid unexpected errors.

Now, you might be wondering how to replicate this issue and test your solution. One useful tool for this is the Android WebView Test App available on the Google Play Store. This app allows you to view console logs within the Android in-app browser, giving you valuable insights into any errors or issues that may arise.

Additionally, if you're troubleshooting via server logs, examining the request header's User-Agent Strings can be helpful. Look for the wv field in the User-Agent String to identify WebView requests specifically. This information can assist in narrowing down the problematic requests and provide a starting point for further investigation.

In conclusion, web application development is not without its challenges, and browser compatibility is one of the crucial aspects to consider. Android in-app browsers can present unexpected issues, especially when it comes to the settings related to JavaScript and DOM storage.

By being aware of this particular issue and implementing a simple condition to check for the availability of localStorage, you can anticipate and tackle the problem head-on, saving yourself valuable troubleshooting time.

That's all for today's episode of Continuous Improvement. I hope you found this discussion helpful. As always, stay curious, stay resilient, and keep pushing the boundaries of your web development skills.

Explaining the Angular Error: 'Expression Changed After It Has Been Checked'

Welcome to "Continuous Improvement," the podcast where we explore tips, tricks, and valuable insights to help you enhance your development skills. I'm your host, Victor, and in today's episode, we'll be discussing a common error message that many developers encounter while working with Angular.

So, picture this: one of our colleagues is deep into developing an Angular frontend application when suddenly, an error message pops up on their screen. The message reads: "ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked."

Now, this error occurred while our colleague was working on implementing a back button feature. Specifically, it happened when navigating from the second page back to the first. The first page had already been rendered once, but it needed to be re-rendered with different initial values.

The root cause of this error lies within Angular's change detection mechanism. Let me explain how it works. After performing each operation, Angular stores the values it used for that operation in the component view's oldValues property. Once all components have been checked, Angular initiates the next digest cycle. However, at this stage, instead of performing operations, it compares the current values with those stored from the previous cycle.

It's important to note that this additional level of checking only occurs in development mode. Angular enforces a unidirectional data flow from the top of the component tree to the bottom. In other words, once a parent component's changes have been processed, no child component should update the parent's properties.

Now, let's move on to possible solutions for resolving this error. One option is to use asynchronous updates, such as setTimeout, to defer the update until the next cycle. This allows Angular to process the changes without triggering the error.

Another solution involves manually triggering change detection at the ngAfterViewInit() lifecycle hook using the _changeDetectorRef.detectChanges() method. The ChangeDetectorRef class provides several methods for managing change detection, including markForCheck(), detach(), detectChanges(), checkNoChanges(), and reattach().

By utilizing these methods, you can manually run change detection and update the child view. Our colleague was pleased to find that by implementing this workaround, the error message was resolved.

In conclusion, if you come across the "ExpressionChangedAfterItHasBeenCheckedError" in your Angular development, remember that it's essential to respect Angular's unidirectional data flow. You can either use asynchronous updates or manually trigger change detection, and these methods should help you overcome the error.

That's it for today's episode of "Continuous Improvement." We hope that sharing this experience and offering practical solutions will assist you in your own development journey.

If you found this episode helpful and would like to dive deeper into the world of continuous improvement, be sure to check out our blog and subscribe to our podcast for future episodes.

Remember, growth is a continuous process, and we're here to support you every step of the way. Thanks for listening, and until next time, keep coding and keep improving.

Writing Unit Test Cases with Karma for Angular Components

Welcome to Continuous Improvement, the podcast where we explore ways to enhance your software development practices. I'm your host, Victor, and in today's episode, we'll be discussing the importance of writing unit tests for your Angular web app.

Many developers often rush to release their code as quickly as possible, sacrificing proper testing in the process. But today, we'll make a strong case for why writing unit tests should be an integral part of your development process, even if it means taking a bit more time.

Let's dive right in. The first compelling reason to write unit tests is their ability to identify issues as early as possible. When multiple teams are working on the same codebase, it's not uncommon for bugs to inadvertently find their way into the app. By having comprehensive unit tests in place, these bugs can be caught early on, preventing those dreaded middle-of-the-night support calls.

Additionally, unit tests enable you to refactor your code with confidence. Instead of dealing with a monolithic system, you can divide your code into manageable, testable units. This modular approach makes it easier to maintain and enhance your app over time.

Now, some companies have policies in place that mandate a certain level of code coverage, often 80% or higher. Writing unit tests not only helps you meet these requirements, but it also provides assurance that your code is well-tested and reliable.

If you're new to unit testing, have no fear. Angular makes it easy to get started. Simply run the following command in your project directory:

npm run test

This will open a Chrome browser window at localhost, on port 9876. From there, you can initiate testing by clicking the "Debug" button.

Now, let's discuss how to write a unit test for a specific piece of code. Imagine you have a login.component.ts file with a login() method that toggles a boolean flag from false to true. To test this functionality, create a file named login.component.spec.ts and write your test cases.

Inside the describe() function, you'll find the test cases. Each case is within its own it() function. For example, in our case, we want to test if the isLogon flag turns true after the login() method is triggered.

By running the tests, you'll see that the first test case passes successfully, ensuring that our code functions as expected. Furthermore, if another developer makes changes that impact this functionality, the test will catch it.

It's important to note that when making API calls during testing, you should avoid calling the actual API. Instead, mock your API call with stub data. This ensures that your tests run consistently, regardless of the state of the actual API.

Remember, unit testing is all about creating reliable, independent tests for individual units of code. By following this practice, you can have confidence in the functionality of your app and prevent regressions during the development process.

Well, that's a wrap for today's episode of Continuous Improvement. I hope you've gained some insight into the significance of writing unit tests for your Angular web app. Remember, taking the extra time to write tests will pay off in the long run, ensuring a more robust and maintainable codebase.

If you enjoyed this episode, make sure to subscribe to our podcast for more valuable insights on software development best practices. Until next time, I'm Victor, signing off. Happy coding!