TECH PLAY

KINTOテクノロゞヌズ

KINTOテクノロゞヌズ の技術ブログ

å…š975ä»¶

This article is the Day 13 entry for the KINTO Technologies Advent Calendar 2025 🎄 Introduction I'm yuki.n ( @yukidotnbysh ) from the Master Maintenance Tool Development Team in the KINTO Backend Development Group, KINTO Development Division, based in the Osaka Tech Lab. Our team develops management systems that integrate with various services. These systems serve not only as administrative tools but also as solutions to business issues. To address these challenges, our management systems cannot be simple CRUD applications. Each system and its business requirements bring various complexities. I believed Railway Oriented Programming would be effective for handling these complexities in our business logic, so I introduced it in a project. Based on this case study, I would like to share the benefits we gained and the issues we faced. What is Railway Oriented Programming? Railway Oriented Programming Railway Oriented Programming is an error handling approach in functional programming proposed by Scott Wlaschin, who runs F# for Fun and Profit and authored Domain Modeling Made Functional . Looking at the article with the same title posted on F# for Fun and Profit , it seemingly have been published at least as early as 2013. In Railway Oriented Programming, functions are compared to railways, with success and failure handling represented as two separate tracks. The following diagram shows multiple of these railways connected together. Each processing step functions as a switch: if successful, processing continues on the success track; if it fails, it switches to the failure track. Once on the failure track, subsequent processing never succeeds, and the error flows through to the end. Specifically, this involves chaining functions that return Result types through a pipeline. Why Did We Adopt Railway Oriented Programming? Our team already had experience developing with Rust, and we adopted it as the backend server development language for this project as well. We designed the project architecture, according to the Clean Architecture diagram—for convenience, simply hereinafter referred to as Clean Architecture. When we introduced Clean Architecture in past projects, we found that the processing in the Use Cases layer (as depicted in the circle diagram above) often results in an unnecessarily complex and clunky structure design. Even when we carved some processing as domain services to combine them into the Entities layer, the readability of the Use Cases layer still suffered. In this situation, I learned about Railway Oriented Programming, which led to its adoption. Railway Oriented Programming with Rust Overall Structure The Use Cases layer we implemented has roughly the following structure. #[derive(Debug, thiserror::Error)] pub enum CreateUserUseCaseError { // Error type definitions } pub trait UsesCreateUserUseCase { /// Workflow fn handle( &self, input: CreateUserInputData, ) -> impl Future< Output = Result<CreateUserOutputData, CreateUserUseCaseError>, > + Send; } pub trait CreateUserUseCase: // Dependencies ProvidesUserFactory + ProvidesUserRepository { } impl<T: CreateUserUseCase + Sync> UsesCreateUserUseCase for T { async fn handle( &self, input: CreateUserInputData, ) -> Result<CreateUserOutputData, CreateUserUseCaseError> { // Chain of functions defined in the railway module } } mod railway { type RailwayResult<T> = Result<T, super::CreateUserUseCaseError>; pub(super) fn validate_input(/* ... */) -> RailwayResult<(Email, UserName)> { /* ... */ } pub(super) async fn check_email_not_exists(/* ... */) -> RailwayResult<(Email, UserName)> { /* ... */ } pub(super) fn build_user(/* ... */) -> RailwayResult<User> { /* ... */ } pub(super) async fn save_user(/* ... */) -> RailwayResult<User> { /* ... */ } pub(super) fn end(/* ... */) -> CreateUserOutputData { /* ... */ } } We use the Cake Pattern introduced in Thinking About DI in Rust — Part 2: Organizing DI Approaches Using Rust (only in Japanese) (I will omit the details as this diverges from the main topic of this blog). We define functions in the railway module and combine them within the handle method of UsesCreateUserUseCase . (Note: In Domain Modeling Made Functional, the part corresponding to the handle method is called workflow, and the arguments are called commands. This article follows that convention.) Let's break down each of these elements. Error Type Definition #[derive(Debug, thiserror::Error)] pub enum CreateUserUseCaseError { // Error type definitions #[error("Email address already exists.")] AlreadyExistsEmail, #[error("Invalid email address.")] InvalidEmail, #[error("Invalid username.")] InvalidUserName, #[error("UserFactoryError")] UserFactoryError(#[from] UserFactoryError), #[error("UserRepositoryError")] UserRepositoryError(#[from] UserRepositoryError), } We always define one error type for each Use Case. In Rust, error types can be defined as Enums. While the standard approach requires implementing the std::error::Error trait, using the thiserror crate simplifies error type definitions. Additionally, when defining with the thiserror crate, setting the #[from] attribute implements the From trait, allowing automatic conversion to the target error type without explicit conversion when the corresponding error occurs. RailwayResult Type mod railway { // Result type specific to this use case type RailwayResult<T> = Result<T, CreateUserUseCaseError>; } Since defining Use Case-specific errors for all functions would be cumbersome, we define a RailwayResult type alias so we only need to specify the return value. railway Module mod railway { /// Validates input values and converts them to value objects. pub(super) fn validate_input( input: CreateUserInputData, ) -> RailwayResult<(Email, UserName)> { let email = Email::try_from(input.email) .map_err(|_| CreateUserUseCaseError::InvalidEmail)?; let name = UserName::try_from(input.name) .map_err(|_| CreateUserUseCaseError::InvalidUserName)?; Ok((email, name)) } /// Confirms that the email address does not already exist. pub(super) async fn check_email_not_exists( (email, name): (Email, UserName), impl_repository: &impl UsesUserRepository, ) -> RailwayResult<(Email, UserName)> { impl_repository .find_by_email(&email) .await .map_err(CreateUserUseCaseError::UserRepositoryError)? .map_or(Ok((email, name)), |_| Err(CreateUserUseCaseError::AlreadyExistsEmail)) } /// Creates a new user. pub(super) fn build_user( (email, name): (Email, UserName), impl_factory: &impl UsesUserFactory, ) -> RailwayResult<User> { impl_factory .build(UserFactoryParams { email, name }) .map_err(CreateUserUseCaseError::UserFactoryError) } /// Saves the user. pub(super) async fn save_user( output: User, impl_repository: &impl UsesUserRepository, ) -> RailwayResult<User> { impl_repository .save(output) .await .map_err(CreateUserUseCaseError::UserRepositoryError) } /// Returns the result and terminates processing. pub(super) fn end( output: User, ) -> CreateUserOutputData { CreateUserOutputData { user: output.into(), } } } We create a module called railway and define the functions that form the tracks within it. This is not a Railway Oriented Programming convention but simply a guide we use for easier identification. In the code for this blog, we assume the following process flows: Validate input values (email address and username) Check email address existence Create User entity Save User entity Convert the saved User entity to a DTO for passing to the upper layer, and end the process Since the return value of the previous function is set as the input value for the next function, the first argument is named output . However, when output is a tuple, we destructure it from the start. This is because handling tuples as-is causes issues with variable ownership. Ideally, only the previous value should be set as the input for the next function, but we determined that the drawbacks outweighed the benefits—such as needing to pass unnecessary values as if through a bucket brigade. That is the reason we adopted a rule allowing new input values to be passed to each function. Overall Workflow The original programming convention uses F#, but Railway Oriented Programming is applicable in any language (or with libraries that supplement it) that has the concept of Result or Either types and the ability to compose functions. Fortunately, Rust comes standard with the following features essential for implementing Railway Oriented Programming, in addition to the Result type: ? operator: Expresses stopping processing when an error occurs map and and_then functions: Function composition By combining these, you can build a pipeline as follows: impl<T: CreateUserUseCase + Sync> UsesCreateUserUseCase for T { async fn handle( &self, input: CreateUserInputData, ) -> Result<CreateUserOutputData, CreateUserUseCaseError> { railway::validate_input(input) .map(|output| { railway::check_email_not_exists(output, self.user_repository()) })? .await .and_then(|output| railway::build_user(output, self.user_factory())) .map(|output| railway::save_user(output, self.user_repository()))? .await .map(railway::end) } } Benefits I Experienced in Practice The following are the benefits I experienced from practicing Railway Oriented Programming. Processing Flow Became Clear, Making Feature Addition Easier Since the workflow contents are connected through a pipeline, you can understand at a glance what processing is being performed. Of course, complex specifications inevitably lead to longer workflows, but even so, tracking the function flow helps us to roughly identify where and what is happening. With each process in the workflow extracted into functions, the scope of each process and its variables also became clear. This helped us add features simply by inserting new functions and modify them by changing the relevant function, which enhanced the system maintainability. Processing Input/Output Can Now Be Expressed Through Types I don't think this is a direct effect of Railway Oriented Programming, but we can now check by type level what data each function's arguments and return values represent. As compile errors can prevent type mistakes, we are able to avoid problems where incorrect values are passed during processing. Simplified Unit Test Scenarios Since implementing all success and failure tests for workflows was extremely labor-intensive and time-consuming, we adopted an approach of thoroughly testing individual railway functions, then only testing the happy path for the workflows. There may be controversies about whether tests for private functions are necessary, but I personally felt it was helpful to test each function in the railway module. So far, I haven't experienced any major problems with this approach. Additionally, as a secondary effect, when features are added, we can confirm there are no issues by adding tests for those functions and ensuring existing tests pass, which I think was beneficial. Issues I Experienced in Practice While gaining benefits from practice, we also faced some issues. Railway Oriented Programming Takes Some Getting Used To For those already familiar with functional languages, this approach probably doesn't feel unusual, but of course there are team members, including myself, who are not. The approach implementation is difficult until you get used to the style of writing. In fact, I struggled quite a bit when examining whether Railway Oriented Programming could be implemented using Rust. Currently, AI has significantly lowered the technical barriers for the implementation, but we humans still need to understand Railway Oriented Programming at some level to determine whether the outcomes are appropriately generated. That’s where supporting team members come in. In practice, the process of reviewing outcomes placed a significant burden on us in the early development phase of this project. Depending on the project's situation and conditions—such as development scale and deadlines—we suggest that you seek another solution instead of Railway Oriented Programming. We May Face fatal runtime error: stack overflow Depending on what kind of process is executed, Stack Overflow errors may occur at runtime. It is particularly troublesome because the error is not detected as a compile error, and you cannot determine which specific location contains issues just by searching around the source code. To find the error cause, you can use rust-lldb to check the stack trace and identify the code where the Stack Overflow error occurred. # Start binary with LLDB rust-lldb target/debug/your-bin # Execute run # Check backtrace when Stack Overflow occurs thread backtrace all When we address issues that are difficult to solve using a method chaining with map and and_then , we take an alternative solution to stop the method and write the processing line by line instead. This is the safest and easiest way to overcome tough issues. async fn handle(&self, input: InputData) -> Result<OutputData, UseCaseError> { railway::begin(self.uow()).await?; let output = railway::validate_email(&input.email)?; let output = railway::authenticate(output, input.password, self.authenticator()).await?; let output = railway::update_last_access(output, self.user_repository()).await?; let output = railway::commit(output, self.uow()).await?; railway::end(output) } The above solution isn't bad in itself, but it eliminates the pipeline through method chaining, creating the risk that we can write codes on a no-holds-barred basis. So, I think it's safest to only switch to this format when we hardly solve issues. As another option, you can expand the stack area by adding a value to the RUST_MIN_STACK variables. However, this merely postpones the issue and may cause the recurrence of the Stack Overflow errors. Therefore, I don’t recommend this solution. In my case, the error occurred in debug builds in the meantime when I executed asynchronous processing within functions defined in the railway module. async/await is syntactic sugar for the Future type, but, according to rust-lang/rust#132050 , the state held by the Future type is expanded to the stack area at runtime, which causes Stack Overflow errors at the execution of many async functions. In this case, I was able to avoid the error by storing the Future type data I wanted to simultaneously process into the Vec type data (because Vec type values are stored in the heap ). Increase in Codes in Return for Clarified Processing Flow When introducing Railway Oriented Programming in Rust, you end up connecting each function with map and and_then . Additionally, you need to define each of those functions, so the overall code volume increases compared to the one when you usually write codes. For example, if Railway Oriented Programming were not applied, the handle method would look like as follows: impl<T: CreateUserUseCase + Sync> UsesCreateUserUseCase for T { async fn handle( &self, input: CreateUserInputData, ) -> Result<CreateUserOutputData, CreateUserUseCaseError> { // Input validation let email = Email::try_from(input.email) .map_err(|_| CreateUserUseCaseError::InvalidEmail)?; let name = UserName::try_from(input.name) .map_err(|_| CreateUserUseCaseError::InvalidUserName)?; // Email duplication check let existing_user = self .user_repository() .find_by_email(&email) .await .map_err(CreateUserUseCaseError::UserRepositoryError)?; if existing_user.is_some() { return Err(CreateUserUseCaseError::AlreadyExistsEmail); } // User creation let user = self .user_factory() .build(UserFactoryParams { email, name }) .map_err(CreateUserUseCaseError::UserFactoryError)?; // Saving user let saved_user = self .user_repository() .save(user) .await .map_err(CreateUserUseCaseError::UserRepositoryError)?; // Result conversion Ok(CreateUserOutputData { user: saved_user.into(), }) } } Without functions, the implementation would be within the handle method (or with some functions partially extracted). Therefore, depending on the case, this approach might be simpler. So for applications with simple processing and mostly branching, it may be safer to unnecessarily adopt Railway Oriented Programming. P.S.: Where’s the Repository Pattern? While not directly related to the main topic of this blog, Domain Modeling Made Functional addresses the repository pattern in a section named “Where’s the Repository Pattern?” The book states the pattern in the functional approach as follows: “...when we model everything as functions and push persistence to the edges, then the Repository pattern is no longer needed.” However, since I couldn't fully grasp the intent and method behind this, we adopted the repository pattern for the code and our project described in this blog. Conclusion This concludes my explanation about practicing Railway Oriented Programming in Rust. Rust is an extremely expressive language with various features, and I feel that adopting Railway Oriented Programming can enhance it further. If you consider adopting Railway Oriented Programming in Rust, I hope this article can serve you as a helpful reference.
Introduction This article is Day 12 of the KINTO Technologies Advent Calendar 2025 . Hello. I'm Matsuo from the Cloud Infrastructure Group. While this wasn't announced at AWS re:Invent, there was an update that I personally found exciting, so I'd like to share it. It's the automatic management feature for Service Quotas. In October 2025, the automatic management settings feature became generally available, enabling notifications when Service Quotas limits are approaching. Then recently, the automatic adjustment mode that was announced at GA was added. Previously, implementing quota monitoring required building a complex architecture yourself, but with this feature, you can set up quota monitoring and automatic adjustment just by clicking through the console. And it's free. However, I should mention upfront that automatic adjustment doesn't support all quotas. In this article, I'll share the specifications and constraints I discovered through actual testing! Update Timeline Date Content October 2025 Automatic management feature added. At launch, only Notify Only mode was GA. Automatic adjustment mode was announced November 2025 Notify and Adjust mode was added Official announcements: AWS Service Quotas の自動クォヌタ管理の䞀般提䟛を開始 - AWS AWS Service Quotas now supports automatic quota adjustment What Makes This Great Before (Traditional Method) To implement quota monitoring, you needed an architecture like this: https://aws.amazon.com/solutions/implementations/quota-monitor/ Source: AWS Solutions Library Monitor metrics with CloudWatch Alarms Lambda functions to call Service Quotas API and send quota increase requests EventBridge trigger configuration SNS for notifications Amazon DynamoDB for managing monitored quotas This resulted in quite a complex architecture that was difficult to build and maintain. After (Automatic Management Feature) Setup complete with just a few clicks in the console Automatic notifications at 80%/95% Automatic Adjustment mode automatically sends quota increase requests No charges for resources that were previously needed for quota monitoring Compared to the previous method, there's no complex configuration. It seems much easier to adopt. Before After Setup effort Required building Lambda/EventBridge/SNS/DynamoDB etc. Just a few clicks in the console Notification timing Set thresholds yourself Automatic notifications at 80%/95% (fixed) Quota adjustment Self-implemented with Lambda etc. Handled by automatic adjustment mode Cost Charges for each resource Free Feature Specifications Notification Timing According to the official documentation , notifications are sent at the following thresholds: When 80% utilization is reached When 95% utilization is reached Important: These thresholds are fixed. When configuring in the console, I thought "Hmm, where do I set the threshold?" but it turns out it's not configurable by design. Customizations like "I want notifications at 70%" aren't possible, so you'd still need to use CloudWatch Alarms for that. Automatic Management Modes Mode Description Notify Only (NotifyOnly) Sends notifications at 80% / 95% Notify and Adjust (NotifyAndAdjust) In addition to notifications, automatically sends quota increase requests Notification Destinations It's integrated with AWS User Notifications, and you can configure the following notification destinations: Email (via SES; events from unsupported regions are sent via Virginia) AWS Console Mobile Application (requires prior app installation and push notification enablement) Chat channels (delivered to Slack/Teams via Amazon Q Developer; requires prior chat client configuration) Pricing It's free to use. There are no additional charges for the automatic management feature itself. AWS User Notifications used for notifications is also free, but if you use SES, mobile push, or Amazon Q Developer behind the scenes, or if you set up custom monitoring with CloudWatch Alarms separately from the automatic management feature, additional charges may apply. Configuration Method Note : As of November 2025, this feature is not yet supported in Terraform Console Configuration Open the Service Quotas console Select Automatic management from the left menu Click Start automatic management Select the automatic management mode (Notify Only or Notify and Adjust) Configure notification settings (optional) Configure exception settings (optional) Review and submit Super easy. CLI Configuration Here's an example configuration for the us-west-2 (Oregon) region. # When specifying User Notifications destination and automatic adjustment aws service-quotas start-auto-management \ --opt-in-level ACCOUNT \ --opt-in-type NotifyAndAdjust \ --notification-arn arn:aws:notifications::xxxxxxxxxxxx:configuration/xxxxx \ --region us-west-2 Official documentation Note : Configuration is required for each region. Setting it in the Oregon region doesn't apply to the Tokyo region. If you want to configure all regions via CLI, you'll need to write a script or similar. Internal Mechanism (Event Pattern) When you configure automatic management, internally an event pattern like the following is set as a detailed filter in AWS User Notifications: { "source": ["aws.health"], "detail-type": ["AWS Health Event"], "detail": { "service": ["SERVICEQUOTAS"], "eventTypeCode": [ "AWS_SERVICEQUOTAS_THRESHOLD_BREACH", "AWS_SERVICEQUOTAS_INCREASE_REQUEST_FAILED", "AWS_SERVICEQUOTAS_APPROACHING_THRESHOLD" ], "eventTypeCategory": ["accountNotification"] } } Meaning of each event type ( official documentation ): eventTypeCode Meaning AWS_SERVICEQUOTAS_APPROACHING_THRESHOLD An adjustable quota is approaching the threshold. In automatic adjustment mode, a quota increase request is sent here AWS_SERVICEQUOTAS_THRESHOLD_BREACH A non-adjustable quota has exceeded the threshold. Since the quota cannot be increased, you need to optimize usage AWS_SERVICEQUOTAS_INCREASE_REQUEST_FAILED The quota increase request failed Actual Testing Test Environment Region: us-west-2 (Oregon) Automatic management mode: Notify and Adjust Required Permissions The permissions required to use the automatic management feature and view permissions are as follows. Permissions required to use: ServiceQuotasFullAccess AWSHealthFullAccess Permissions required to view: AWSHealthFullAccess Checking Supported Quotas I checked from View supported quotas in the console, but perhaps because it was just after release, the coverage was more limited than I expected. I hope the supported resources will expand in the future. Examples of supported services: AWS Systems Manager Amazon EC2 WAF (Regional only) ELB Lambda Route53 IAM Examples of unsupported services: VPC API Gateway EventBridge Amazon Simple Email Service (SES) For global services like Route53 and IAM, it seems only notifications are available and automatic adjustment is not supported Verifying Automatic Adjustment Behavior I tested with AWS WAF's Maximum regex pattern sets per account in WAF for regional. This quota has a default value of 10, so creating 8 would reach 80%. Test Procedure 1. Preparation First, configure automatic management in Service Quotas. Enable automatic management in Service Quotas with Notify and Adjust mode (I didn't capture the screen when setting up in the Oregon region, so this is the settings screen from the Virginia region) Move to notification settings. Here you configure AWS User Notifications. This time, I set up Amazon Q Developer to send notifications to a Slack channel. Note : You can select the event source region, but since you can only receive events from regions where automatic management is enabled, you need to configure automatic management for each region you want to monitor (it's a bit confusing because automatic management and AWS User Notifications are separate settings) You can select services to exclude from automatic management, but this time I created it without any settings 2. Create Regex Pattern Sets Next, create regex pattern sets to trigger the threshold. WAF console -> Regex pattern sets -> Create regex pattern set Region: us-west-2 (Oregon) *Select Regional Create 8 with arbitrary names 3. Check Utilization Service Quotas -> AWS WAF -> Maximum regex pattern sets per account in WAF for regional Confirmed that utilization was at 80% 4. Wait for Notification/Automatic Adjustment Wait a few minutes... Test Results Time until notification/automatic adjustment: Not confirmed precisely, but completed in a few minutes Quota value: Changed from 10 to 11. You can't specify the quota value after automatic adjustment, but it seems to update to a quota that falls below 80%. Slack notifications are hard to read Newline codes ( \n ) are displayed as-is, making long text appear as one block. Also, the notification body doesn't include specific information about which quota is approaching the threshold, so you need to check the Affected resources tab in the AWS Health Dashboard via the link attached to the notification. As a notification, you can tell something is approaching the threshold, but it takes extra effort to check the details. Notes and Constraints 1. Limited Supported Quotas Honestly, this is the biggest constraint I felt at the moment. It's disappointing that quotas for basic services like VPC and SES aren't supported. Currently, automatic management of Service Quotas alone won't handle all quota-related issues. We'll have to wait for the supported quotas to expand. 2. Thresholds Cannot Be Customized The 80%/95% thresholds are fixed. Requirements like "I want early notifications at 50%" or "No notifications needed until 100%" cannot be accommodated. 3. AWS Organizations Support Not Implemented As of November 2025, bulk configuration at the Organizations level is not supported, so you need to configure each account individually. There's an --opt-in-level option in the CLI command, so I hope it will be supported eventually. 4. Automatic Adjustment Approval Is Not Guaranteed Automatic adjustment is merely a feature that automatically sends quota increase requests. Whether the request is approved by AWS is a separate matter, and it may be rejected. 5. Notifications/Automatic Increases May Take Time Right after the quota was raised to 11, I added 3 more regex patterns for a total of 11, bringing utilization to 100%. However, this time there was no notification and no automatic adjustment. But several hours later, I confirmed that automatic adjustment had occurred, so it seems that notifications and automatic adjustments may take time in some cases. Conclusion The automatic management feature for Service Quotas is personally a really great update that significantly lowers the barrier to quota monitoring. It's free to start and can be configured with just a few clicks from the console. There are still constraints like limited supported quotas and no IaC (Terraform) support, but I found that for supported quotas, it can actually reduce the effort required for quota expansion work. At our company, we also operate a custom-built quota management tool, and we plan to continue using it for quotas that aren't supported by the automatic management feature. I hope you found this article helpful.
はじめに この蚘事は KINTOテクノロゞヌズ Advent Calendar 2025 の12日目の蚘事です🎅🎄 こんにちは。クラりドむンフラ所属の束尟です。 AWS re:Inventで発衚された新機胜ではないですが、 個人的に熱いず感じたアップデヌトがあったので玹介したす。 Service Quotasの自動管理機胜です。 2025幎10月に「自動管理蚭定」機胜がGAずなり、 Service Quotasの䞊限倀が近づくず通知が飛ぶようになりたした。 そしお先日、GAのタむミングで予告されおいた「自動調敎」モヌドが远加。 これたでクォヌタ監芖を実装しようずするず、耇雑な構成を自前で甚意する必芁がありたしたが、 この機胜を䜿えばコン゜ヌルからポチポチするだけでクォヌタの監芖から自動調敎たでやっおくれたす。 しかも無料です。 ただ、先に蚀っおおくず自動調敎に぀いお、「党郚のクォヌタに察応しおいるわけではない」ずいう制玄がありたす。 この蚘事では、実際に怜蚌しおわかった仕様や制玄を曞いおいきたす アップデヌトの経緯 日付 内容 2025幎10月 自動管理機胜の远加。远加時は「通知のみ」モヌドがGA。自動調敎モヌドの予告あり 2025幎11月 「通知ず自動調敎」モヌドが远加 公匏アナりンス AWS Service Quotas の自動クォヌタ管理の䞀般提䟛を開始 - AWS AWS Service Quotas now supports automatic quota adjustment 䜕が嬉しいのか Before埓来の方法 クォヌタ監芖を実装するには、こんな構成が必芁でした https://aws.amazon.com/solutions/implementations/quota-monitor/ 出兞: AWS Solutions Library CloudWatch Alarmsでメトリクスを監芖 Lambda関数でService Quotas APIを叩いお䞊限緩和リク゚ストを送信 EventBridgeでトリガヌ蚭定 SNSで通知 Amazon DynamoDB 監芖察象のクォヌタの管理 これだずかなり耇雑な構成になるので構築、運甚が倧倉でした。 After自動管理機胜 コン゜ヌルで数クリックで蚭定完了 80%/95%で自動通知 「自動調敎」モヌドなら自動で䞊限緩和リク゚ストたで送っおくれる 今たでクォヌタ監芖で必芁だったリ゜ヌスの料金の発生はなし 埓来たでの方法ず比べ、耇雑な蚭定は無し。かなり導入がしやすそうです。 Before After 蚭定の手間 Lambda/EventBridge/SNS/DynamoDB等の構築が必芁 コン゜ヌルで数クリック 通知タむミング 自分で閟倀を蚭定 80%/95%で自動通知固定 クォヌタ調敎 Lambda等で自前実装 自動調敎モヌドで察応 料金 各リ゜ヌスの料金が発生 無料 機胜の仕様 通知タむミング 公匏ドキュメント によるず、以䞋のタむミングで通知が送信されたす 80% 䜿甚率に達した時 95% 䜿甚率に達した時 重芁この閟倀は固定です。 コン゜ヌルで蚭定するずき「あれ、閟倀蚭定するずころないな」っお思ったんですが、そもそも倉曎できない仕様でした。 「70%で通知が欲しい」みたいなカスタマむズはできないので、その堎合は埓来通りCloudWatch Alarmsを䜿う必芁がありたす。 自動管理モヌド モヌド 説明 通知のみNotifyOnly 80% / 95%で通知を送信 通知ず自動調敎NotifyAndAdjust 通知に加えお、自動でクォヌタ増加リク゚ストを送信 通知先 AWS User Notificationsず統合されおいお、以䞋の通知先を蚭定できたす EメヌルSES経由、サポヌトされおいないリヌゞョンからのむベントはバヌゞニア経由で送信 AWS コン゜ヌルモバむルアプリケヌション事前にアプリのむンストヌルずプッシュ通知の有効化が必芁 チャットチャネルAmazon Q Developer経由でSlack/Teamsに配信、事前にチャットクラむアントの蚭定が必芁 料金 無料で䜿甚できたす。 自動管理機胜自䜓には远加料金はかかりたせん。 通知に䜿われるAWS User Notificationsも無料ですが、 裏で呌び出しおいるSES・モバむルプッシュ・Amazon Q Developerの䜿甚や、 自動管理機胜ずは別にCloudWatch Alarmsで独自の監芖を蚭定する堎合は、別途料金がかかる堎合がありたす。 蚭定方法 📝 補足 : 2025幎11月時点では本機胜は珟圚Terraformでは察応しおおりたせん コン゜ヌルでの蚭定 Service Quotasコン゜ヌルを開く 巊メニュヌから「自動管理」を遞択 「自動管理を開始」をクリック 自動管理モヌドを遞択「通知のみ」たたは「通知ず自動調敎」 通知蚭定を構成オプション 䟋倖蚭定を構成オプション 確認しお送信 めちゃくちゃ簡単。 CLIでの蚭定 以䞋はus-west-2オレゎンリヌゞョンでの蚭定䟋です。 # User Notificationsの通知先ず自動調敎を指定する堎合 aws service-quotas start-auto-management \ --opt-in-level ACCOUNT \ --opt-in-type NotifyAndAdjust \ --notification-arn arn:aws:notifications::xxxxxxxxxxxx:configuration/xxxxx \ --region us-west-2 公匏ドキュメント ⚠ 泚意 : リヌゞョンごずに蚭定が必芁です。オレゎンリヌゞョンで蚭定しおも、東京リヌゞョンには適甚されたせん。 CLIで党リヌゞョン蚭定する堎合はスクリプトを組むなどの察応が必芁です。 内郚の仕組みむベントパタヌン 自動管理を蚭定するず、内郚的にはAWS User Notificationsの詳现フィルタヌずしお以䞋のようなむベントパタヌンが蚭定されたす { "source": ["aws.health"], "detail-type": ["AWS Health Event"], "detail": { "service": ["SERVICEQUOTAS"], "eventTypeCode": [ "AWS_SERVICEQUOTAS_THRESHOLD_BREACH", "AWS_SERVICEQUOTAS_INCREASE_REQUEST_FAILED", "AWS_SERVICEQUOTAS_APPROACHING_THRESHOLD" ], "eventTypeCategory": ["accountNotification"] } } 各むベントタむプの意味 公匏ドキュメント  eventTypeCode 意味 AWS_SERVICEQUOTAS_APPROACHING_THRESHOLD 調敎可胜な クォヌタが閟倀に近づいた。自動調敎モヌドならここでクォヌタの匕き䞊げリク゚ストが送信される AWS_SERVICEQUOTAS_THRESHOLD_BREACH 調敎できない クォヌタが閟倀を超過。クォヌタの匕き䞊げできないので䜿甚率を最適化する必芁あり AWS_SERVICEQUOTAS_INCREASE_REQUEST_FAILED クォヌタの匕き䞊げリク゚ストが倱敗した 実際に怜蚌しおみた 怜蚌環境 リヌゞョンus-west-2オレゎン 自動管理モヌド通知ず自動調敎 必芁な暩限 自動管理機胜を䜿甚するために必芁な暩限、および閲芧暩限は以䞋の通りです。 䜿甚するために必芁な暩限 ServiceQuotasFullAccess AWSHealthFullAccess 閲芧するために必芁な暩限 AWSHealthFullAccess 察応クォヌタの確認 コン゜ヌルの「サポヌトされおいるクォヌタを衚瀺」から確認しおみたんですが、リリヌス盎埌だからか思ったより察応範囲が限定的でした。 今埌、察応するリ゜ヌスが増えるこずを期埅したいです。 察応しおいたサヌビスの䟋 AWS Systems Manager Amazon EC2 WAFRegionalのみ ELB Lambda Route53 IAM 察応しおいなかったサヌビスの䟋 VPC API Gateway EventBridge Amazon Simple Email ServiceSES Route53やIAMなどのグロヌバルサヌビスの堎合、通知のみできるようで自動調敎には察応しおいない印象でした 自動調敎の動䜜確認 AWS WAFの「Maximum regex pattern sets per account in WAF for regional」で怜蚌しおみたした。 このクォヌタはデフォルト倀が10なので、8個䜜成すれば80%に到達したす。 怜蚌手順 1. 事前準備 たずはService Quotasで自動管理の蚭定を行いたす。 Service Quotasの自動管理を「通知ず自動調敎」モヌドで有効化 オレゎンリヌゞョンで蚭定した際、キャプチャしおなかったのでバヌゞニアリヌゞョンでの蚭定画面になりたすmm 通知蚭定に移りたす。ここではAWS User Notificationsの蚭定を行いたす。 今回はAmazon Q Developerを甚意し、Slackチャンネルに通知するようにしたした。 ⚠ 泚意 : むベント゜ヌスのリヌゞョンを遞択できたすが、自動管理を有効にしおいるリヌゞョンのむベントしか受け取れないため、 監芖したいリヌゞョンごずに自動管理の蚭定が必芁です自動管理ずAWS User Notificationsは別蚭定だからちょっずややこしい 自動管理の䟋倖ずするサヌビスを遞択できたすが、今回は䜕も蚭定しないたた䜜成 2. 正芏衚珟パタヌンセットを䜜成 次に正芏衚珟パタヌンセットを䜜成し、閟倀に匕っかかるようにしたす。 WAFコン゜ヌル → 正芏衚珟パタヌンセット → 「Create regex pattern set」 リヌゞョンus-west-2オレゎン※Regionalを遞択 適圓な名前で8個䜜成 3. 䜿甚率の確認 Service Quotas → AWS WAF → 「Maximum regex pattern sets per account in WAF for regional」 䜿甚率が80%になっおいるこずを確認したした 4. 通知/自動調敎の発動を埅぀ 数分埅機... 怜蚌結果 通知/自動調敎反映たでの時間正確には確認できおいたせんが、数分皋床で完了 クォヌタ倀10→11に。自動調敎埌のクォヌタ倀の指定はできたせんが、80%を切るようなクォヌタに曎新しおくれそう 通知に関しお、Slack通知は芋づらい 改行コヌド \n がそのたた衚瀺されおしたい、長文が䞀塊になっおいたす。 たた、通知本文には「どのクォヌタが閟倀に近づいたか」の具䜓的な情報が含たれおおらず、 通知文に添付のリンクからAWS Health Dashboardの「Affected resources」タブを確認する必芁がありたす。 通知ずしおは「䜕か閟倀に近づいた」こずはわかるが、詳现確認には䞀手間かかる印象です。 泚意点・制玄 1. 察応クォヌタが限定的 正盎、今のずころこれが䞀番倧きな制玄だなず感じたした。 VPCやSESずいった基本的なサヌビスのクォヌタが察応しおいないのは残念です。 珟時点ではService Quotaの自動管理だけでは「クォヌタ呚りの調敎がすべお解決」ずはならない。 察応クォヌタの拡倧を今埌埅぀しかないです。 2. 閟倀のカスタマむズ䞍可 80%/95%の閟倀は固定。「50%で早めに通知が欲しい」「100%になるたでは通知䞍芁」みたいな芁件には察応できたせん。 3. AWS Organizations察応は未実装 2025幎11月時点では、Organizations単䜍での䞀括蚭定には察応しおいないため、 各アカりントで個別に蚭定する必芁がありたす。 CLIコマンドには --opt-in-level ずいうオプションがCLIであるのでい぀か察応されるこずを期埅したいです。 4. 自動調敎の承認は保蚌されない 自動調敎はあくたで「䞊限緩和リク゚ストを自動送信する」機胜です。 リク゚ストがAWSに承認されるかどうかは別の話で、拒吊される可胜性もありたす。 5. 通知・自動緩和たでに時間がかかる堎合がある クォヌタが11に䞊がった盎埌、正芏衚珟パタヌンを3぀远加しお合蚈11個にしお䜿甚率を100%にしおみたした。 が、今床は通知もされず、自動調敎もありたせんでした。 しかし数時間埌、自動調敎されおいるこずが確認できたこずから、 堎合によっおは通知や自動調敎たでに時間がかかるこずがあるようです。 たずめ Service Quotasの自動管理機胜は、クォヌタ監芖のハヌドルを倧きく䞋げおくれる個人的にはかなり良いアップデヌトです。 無料で始められお、コン゜ヌルから数クリックで蚭定できたす。 察応クォヌタがただ限定的だったり、IaCTerraform未察応だったりずただただ制玄はありたすが、 サポヌトされおいるクォヌタに関しおは実際にクォヌタ拡匵䜜業の工数を削枛できるこずがわかりたした。 匊瀟では独自実装のクォヌタ管理ツヌルも運甚しおおり、 自動管理機胜でサポヌトされおいないクォヌタに぀いおは匕き続きそちらで察応しおいく予定です。 以䞊、この蚘事が誰かの参考になれば幞いです。
This article is the 12th entry in the KINTO Technologies Advent Calendar 2025 . Introduction I'm Uehara ( @penpen_77777 ), a backend engineer in the FACTORY E-commerce Development Group at KINTO Technologies. I participated in Techbook Fest 19 as part of the KINTO Technologies Writing Club, a circle (a small creator group) formed by volunteer engineers at KINTO Technologies. In this article, I'll share our journey of creating a technical book with company volunteers. Here's the introduction to the new book we created! It's free. If you're interested, please download the digital version. https://blog.kinto-technologies.com/posts/2025-10-31-techbookfest19-announcement/ https://techbookfest.org/product/qCPrJpWLmKnLt7eWVd9zJ6 The Beginning I had written technical books before and had been wanting to write one again after a long break. While tech blogs and presentations are sufficient for getting your writing read by others, there's something deeply satisfying about seeing your writing become a physical book. Since our company had never organized a circle of employee volunteers to write technical books, I decided to take on this challenge. Building the Team Finding Supportive People I started working on this in June, about six months before participating in Techbook Fest. Finding supportive people early on is extremely helpful when moving tasks forward. Our company has a Developer Relations Group that can provide consultation for employees' external technical communications. I truly believe we were able to accomplish this thanks to the Developer Relations Group. I'm very grateful. Finding People Interested in Writing Since participating in Techbook Fest was a first-time endeavor, I was worried about whether we could actually gather participants. Announcing a call for participants company-wide out of the blue felt like a high hurdle. So I decided to reach out to people who seemed interested and gather those who were highly likely to participate in advance. At our company, many people use times (personal update channels) on Slack, which is great for casually expressing your thoughts. By hinting at my desire to exhibit at Techbook Fest, I was able to know who might be interested. About five people gathered at this point, so I felt reassured that even if a company-wide announcement didn't attract more participants, we could still make the project work. Making the Announcement I created a simple slide to recruit writers company-wide. It summarized information about Techbook Fest and the benefits of writing, designed to encourage participation. The official sponsorship materials were helpful for introducing the scale of the event. https://techbookfest.org/assets/tbf18/for-sponsors.pdf Selecting a Printing Company Since we were creating a physical book, we needed to select a printing company. Techbook Fest has designated backup printing companies, and by submitting your manuscript to one of them, they handle everything from printing to delivering it to the venue. It’s a very convenient system. The backup printing companies are Nikko Kikaku and Neko no Shippo, and we chose Nikko Kikaku for this project. Selecting and Configuring the Typesetting System A typesetting system converts manuscripts into a print-ready format. Re:VIEW is commonly used as a typesetting system for technical books. By processing text files written in the proprietary Re:VIEW format through Re:VIEW, they are converted into formats commonly used for e-books such as EPUB and PDF. The benefit is that the separation of page style settings from content allows writers to focus on the essential task of writing without worrying about appearance. For this project, we used Vivliostyle^[It reads "kumihan," but I always mistakenly read it as "soban." https://ja.wikipedia.org/wiki/%E7%B5%84%E7%89%88] as our typesetting system. https://vivliostyle.org/ja/ Vivliostyle's distinguishing feature is the ability to change appearance using CSS, so anyone familiar with frontend technologies should be able to use it without difficulty. Since setting everything up from scratch would be challenging, we utilized a template distributed by YUMEMI. We're very grateful for the distribution of such a template. Thank you! https://github.com/yumemi-inc/daigirin-template Based on the above template, we made the following modifications: Updated the title, publisher, and colophon for our technical book Changed the paper size to B5^[This is when I learned that B5 comes in both JIS B5 and ISO B5. Be careful, as they have different dimensions despite both being called B5.] Enabled automatic table of contents output and adjusted the template for sections that weren't outputting correctly Registering Your Circle for Techbook Fest While there's a lot to prepare, don't forget to submit your circle participation application. It might be a good idea to add it to your calendar. Having Writers to Write I created and shared guidelines so writers wouldn't struggle with the writing process. Overall schedule and what to do in each phase Writing methods (Vivliostyle Flavored Markdown^[A notation that extends common markdown to express book-specific structures. https://vivliostyle.github.io/vfm/#/] syntax, PDF preview methods, etc.) Manuscript Management We managed manuscripts on GitHub, with each writer branching off from the main branch to write. Having each writer create a PR is convenient for tracking progress. Using Slack Lists To prevent topic overlap within the book, I asked writers to add their topics to a Slack list once decided. It's also helpful to include other information you want to track, such as self-introductions for the appendix and writing status. While we could have managed this with tables in Confluence, I personally check Slack more frequently, so I managed it using Slack lists, which can be used similarly to Notion databases. Getting the Book Cover Created When I was wondering whether to create the book cover myself, one of the participants suggested asking designers from the Creative Office for help. Since we wanted to create it in a fun, collaborative way, we decided to hold a Generative AI × Live Visual Creation event where designers would create the cover using generative AI. The event held to create this technical book: Vibe Painting Hour #01 Five designers joined the event and, while participants watched, used generative AI to create covers within a one-hour time limit. Despite my vague request to create a cover with a sense of mobility and futuristic feel, all five final cover proposals were of such high quality that choosing only one seemed like a waste! Through participant voting and my judgment, the cover for this technical book was selected. ![衚玙](/assets/blog/authors/uehara/2025-10-24-techbookfest19-announcement/cover.webp =300x) The cover of our new book, TECH BOOK By KINTO Technologies Vol.01. Setting Manuscript Deadlines This is important. The submission deadline is absolute, so you should set it with plenty of buffer time. Also, it's best to assume that manuscripts won't be ready on the first try. You should set not just one deadline but two or three backup deadlines, with minimum requirements for each deadline. Having Writers Review Each Other's Work We had writers review each other's work. The review items followed our external communication review guidelines, while also keeping in mind that the final product would be a physical book. Since you can't even start reviewing without all manuscripts being submitted, I recommend setting the second deadline early. Proofreading After compiling all manuscripts, we adjusted the overall page layout. After noticing the font size was too small, I made it slightly larger. The table of contents had grown to 10 pages, so I narrowed down the items to include. We also had to adjust the page count to be a multiple of four, or the submission wouldn't be accepted. This caused display issues in some chapters, so we asked writers to make corrections. Since we were working under time pressure, we decided to set the third deadline early. Submission Once the PDF file for submission was ready, we submitted it to the printing company. With Nikko Kikaku, we paid in advance and then sent the cover and body data via a form. If there were any data issues, they would point them out and we'd need to resubmit. This time there were no issues and submission went smoothly. Booth Setup Simulation With help from a Developer Relations team member who was experienced with booth setup, we carried out a simulation the day before. By visualizing the booth in advance and identifying what we needed, I think we avoided the situation where we'd look at the actual booth on the day and feel like "hmm, this isn't quite what I imagined..." The Day of the Offline Event When I first saw the finished book at the offline venue, I thought it was thicker than expected. Since we distributed this thick book for free, we heard surprised reactions from attendees. We printed about 300 copies and distributed them all, so I think it was a great result for our first participation! https://x.com/KintoTech_Dev/status/1989983294059168095 Depositing at the National Diet Library The day after Techbook Fest, we deposited the book at the National Diet Library. Apparently, publications issued for distribution within Japan are generally subject to legal deposit, and even technical doujinshi like ours can be deposited. https://www.ndl.go.jp/jp/collect/deposit/deposit.html Publications issued for distribution within Japan are, in principle, all subject to legal deposit (see "Overview of the Legal Deposit System"). When you deposit publications with the National Diet Library, their bibliographic data becomes searchable as the "National Bibliography" through the NDL Search. Additionally, they are widely used as library materials, preserved as cultural assets shared by the nation, and passed down to future generations as records of the Japanese people's intellectual activities. I also referenced the following article for the deposit process. https://qiita.com/mitsuharu_e/items/850ae6688a94ea3d67f3 : Off to Deposit Being a central government location, various train lines converge here, including the Ginza, Marunouchi, and Hanzomon lines. Exit 2 is the closest to the National Diet Library, so depending on your line, you may need to walk a bit. Area directory near the station. Exit 2 is the closest. I took the Ginza Line, so I had to walk quite a bit. National Diet Library sign. There's a notice that those depositing materials should go to the service entrance (west gate). Enter through the National Diet Library's service entrance (west gate) and tell the security guard at the building entrance that you want to deposit books, and they'll handle the procedure. National Diet Library service entrance (west gate) At the deposit counter, hand over the book and fill out the paperwork. Once the procedure is complete, they'll give you a receipt. Material receipt After depositing, the book appears in National Diet Library Search results and becomes available to read as a resource at the National Diet Library. https://ndlsearch.ndl.go.jp/books/R100000002-I034410569 Reflections After Completion Don't Try to Do Everything Alone; Reach Out for Help Even creating just one book involves many decisions and tasks, and handling everything alone is quite difficult. It's better to delegate tasks that others can do, or that others can do better than you. The Technical Book Expanded Our Connections As well as interacting with attendees at the Techbook Fest offline venue, I believe the connections among company volunteers deepened through creating the technical book. Set Multiple Early Deadlines Even though I thought I had set deadlines with plenty of buffer, I still ended up feeling short on time. Next time, I plan to build in an even larger margin. Setting multiple deadlines is important. Summary While there were many challenges on the road to participating in Techbook Fest 19, looking back, we gained a lot and it was truly enjoyable. If this article has piqued your interest, please try taking on the challenge of creating a technical book. We'll keep working hard so that we can show up even stronger at future Techbook Fests! https://techbookfest.org/product/qCPrJpWLmKnLt7eWVd9zJ6
この蚘事は KINTOテクノロゞヌズ Advent Calendar 2025 の12日目の蚘事です🎅🎄 はじめに KINTOテクノロゞヌズのFACTORY EC開発グルヌプでバック゚ンド゚ンゞニアをやっおいる、うえはら( @penpen_77777 )です。 技術曞兞19に、KINTOテクノロゞヌズの有志゚ンゞニアによるサヌクル「KINTOテクノロゞヌズ執筆郚」ずしお参加しおきたした。 この蚘事では瀟内有志で技術曞を䜜り䞊げるたでの道のりをご玹介したす。 ちなみに今回䜜った新刊の玹介はこちらになりたす無料なのでもしご興味があれば電子版のダりンロヌドよろしくお願いしたす。 https://blog.kinto-technologies.com/posts/2025-10-31-techbookfest19-announcement/ https://techbookfest.org/product/qCPrJpWLmKnLt7eWVd9zJ6 始たり 私はこれたでに技術本の執筆をしたこずがあり、久々に技術本を曞きたいなずいう思いがありたした。 文章を他人に読んでもらうだけなら技術ブログや登壇でも十分なのですが、やっぱり自分の曞いた文章が本になるのは感慚深いものがありたす。 匊瀟では瀟員有志でサヌクルを組んで技術曞を曞くずいうこずがなかったため、今回チャレンゞしおみるこずにしたした。 仲間を䜜る サポヌトしおくれそうな人を芋぀ける 技術曞兞ぞ参加する玄半幎前の6月くらいから動き出し始めたした。 早めにサポヌトしおくれそうな人を芋぀けおおくずタスクを進めおおく䞊で非垞に心匷いです。 匊瀟には技術広報グルヌプがあり瀟員の技術的な倖郚発信の盞談に乗っおもらえたす。 やり遂げるこずができたのも技術広報のおかげだなず思っおいたす。本圓に感謝しおいたす。 執筆に興味がありそうな人を芋぀ける 技術曞兞ぞの参加は初めおの詊みずいうこずもあっお、本圓に参加者が集たるのかが䞍安なずころでした。 いきなり党瀟に参加者募集の告知をしお、参加者を集めるのはハヌドルが高いず感じおいたした。 そこで興味がありそうな人に声をかけおあらかじめ参加する確床の高い人を集めるこずにしたした。 匊瀟ではSlack䞊でtimes(分報チャンネル)を掻甚する人も倚く、気軜に自分の気持ちを衚明できるのが良いずころです。 技術曞兞ぞの出展したい気持ちを匂わせるこずで、興味がありそうな人が誰か知るこずにしたした。 ここで5人くらい集たったので、最悪党瀟告知しお集たらなくおも䌁画ができそうだなず思い、安心しお進めるこずができたした。 告知する 党瀟で執筆者を募集できるよう簡単なスラむドを䜜りたした。 技術曞兞に぀いお、執筆に察するメリット等をたずめおいお参加を蚎求する内容ずなっおたす。 むベントの芏暡を玹介するのに公匏から出おいる協賛資料が参考になりたした。 https://techbookfest.org/assets/tbf18/for-sponsors.pdf 印刷所の遞定をする 物理本を䜜るため印刷所の遞定が必芁になりたす。 技術曞兞ではバックアップ印刷所が蚭定されおおり、このバックアップ印刷所に入皿するこずで印刷から䌚堎ぞの玍品たでやっおいただける非垞に䟿利な仕組みずなっおいたす。 バックアップ印刷所には日光䌁画・ねこのしっぜがあるのですが、今回は日光䌁画さんを遞択したした。 組版システムを遞定する・蚭定を敎える 組版システムずは印刷向けの芋た目に倉換するものを蚀いたす。 技術曞向けの組版システムずしおはRe:VIEWがよく䜿甚されたす。 独自の「Re:VIEWフォヌマット」で蚘述されたテキストファむルをRe:VIEWに通すこずによっお、電子曞籍ずしおよく甚いられるEPUB、PDFなどに倉換されたす。 玙面䞊のスタむルの蚭定ず内容が分離しおいるこずで、執筆者が芋た目を意識せずどんな内容の文章を曞くかずいう本質的な営みに集䞭できるのがメリットです。 今回は組版システム^[読み方は「くみはん」らしいです。毎回そばんず読んでしたいたす。 https://ja.wikipedia.org/wiki/%E7%B5%84%E7%89%88]ずしお「Vivliostyle」を䜿甚したした。 https://vivliostyle.org/ja/ VivliostyleはCSSを䜿っお芋た目を倉えられるのが特城で、フロント゚ンド技術に詳しい方であれば難なく䜿いこなせるかず思いたす。 れロから蚭定を敎えるのは倧倉なので、今回はゆめみさんが配垃されおいるテンプレヌトを掻甚させおいただきたした。 このようなテンプレヌトを配垃いただき倧倉感謝しおおりたす。ありがずうございたした https://github.com/yumemi-inc/daigirin-template 䞊蚘のテンプレヌトを元に以䞋の修正を入れたした タむトル・発行者・奥付けを今回の技術曞向けの内容に修正 玙面サむズをB5ぞ倉曎^[ここでB5にはJIS B5ずISO B5があるこずを知りたした。同じB5でもサむズが異なるのでご泚意を。] 目次の自動出力を有効化、うたく出力できおいない箇所はテンプレヌトを調敎 技術曞兞ぞサヌクル登録する 色々準備がある䞭で忘れそうにはなりたすが、サヌクル参加申蟌は忘れずに行いたしょう。 カレンダヌに予定を入れたりする方が良いかもですね。 執筆者に曞いおもらう 執筆者が執筆に困らないようガむドラむンを䜜成し、共有したした。 党䜓的なスケゞュヌルずそれぞれのフェヌズでやるこず 執筆方法(Vivliostyle Flavored Markdown^[䞀般的なmarkdownを曞籍特有の構造も衚珟できるよう拡匵した蚘法。 https://vivliostyle.github.io/vfm/#/]の蚘法 , PDFのプレビュヌ方法等) 原皿管理 原皿管理はGitHubで実斜し、mainブランチからそれぞれ執筆者ごずにブランチを切っおもらい執筆䜜業を行っおもらいたした。 執筆者ごずにPRを出しおおくず進捗確認ができお䟿利です。 Slackリストを掻甚する 曞籍内でテヌマが被らないようテヌマが決たったらSlackのリストにたずめおもらうようにお願いしたした。 他にも巻末に入れる自己玹介文や執筆状況等、自分で把握しおおきたい内容を入れおおくず芋通しが良いです。 Confluenceで衚を䜿っお管理しおも良かったのですが、個人的にはSlackの方が芋る頻床が高いのずNotionのデヌタベヌスっぜく䜿えるSlackリストで管理しおいたした。 衚玙を䜜成しおもらう 技術曞の衚玙を自分で䜜るか悩んでいたずころ、参加者の䞀人から「クリ゚むティブ宀のデザむナヌさんに協力をお願いしおみおは」ず提案がありたした。 せっかくなら人を集めおワむワむしながら䜜っおいこうずいうこずで、デザむナヌが生成AIを駆䜿しお衚玙を䜜成する「生成AI × ラむブペむンティング」むベントの開催が決たりたした。 今回の技術曞を䜜るのに開催されたむベント「Vibe Painting Hour #01」 むベントでは5人のデザむナヌの方に来おいただき、参加者が芋守る䞭、1時間ずいう制限時間で生成AIを駆䜿しお衚玙を䜜っおいただきたした。 「モビリティ感があっお未来感のある衚玙を䜜る」ずいう、うえはらからの䞭身がふわふわした䟝頌にもかかわらず、最終的に䞊がっおきた衚玙の5案はどれもクオリティが高く1぀に決めるのが勿䜓無いくらいでした 参加者の倚数決ずうえはらの刀断によっお、今回の技術曞の衚玙が遞ばれたした。 ![衚玙](/assets/blog/authors/uehara/2025-10-24-techbookfest19-announcement/cover.webp =300x) 新曞「TECH BOOK By KINTO Technologies Vol.01」の衚玙。 原皿の締切を蚭ける こちらは重芁です。入皿期限は絶察的な締切なので、それに合わせお䜙裕を持っお蚭定した方が良いです。 たた䞀発で原皿が揃うのはなかなかないず思った方が良いでしょう。 締切は1個だけではなくバックアップのためさらに2、3個蚭け、それぞれの締切で達成しおほしい最䜎ラむンを蚭定した方が良いでしょう。 執筆者間でレビュヌしおもらう 執筆者間でレビュヌしおもらいたした。 レビュヌ項目は瀟倖ぞの倖郚発信時のレビュヌ項目に準じ぀぀、最終的には物理本にするこずを意識しお項目を蚭定しおおくず良いでしょう。 原皿が出揃わないずレビュヌすらできないので、締切は早めに蚭定しおおくこずをお勧めしたす(2回目)。 æ ¡æ­£ 党員分の原皿を取りたずめた䞊で、党䜓の玙面の芋栄えを調敎しおいきたした。 文字サむズが小さいこずに気づいたため少し倧きくしたり、目次だけで10ペヌゞず倚くなっおしたったので、目次に出す項目を絞ったりしたした。 ペヌゞ数を4の倍数に調敎しおおかないず入皿を受け付けおくれないので、うたいこずペヌゞ数の調敎もしおいきたした。 この圱響で衚瀺が厩れおしたった章もあったので、執筆者に修正をお願いしおいたした。 時間がない䞭での䜜業だったので、締め切りは早めに蚭定しおおきたしょう(3回目)。 入皿 入皿甚のPDFができたら印刷所に入皿しおいきたす。 日光䌁画さんでは先に入金しおおき、入皿甚の衚玙ず本文デヌタをフォヌムで送信すればOKでした。 ここでデヌタの䞍備があれば指摘があり再提出するこずになりたすが、今回は特に䞍備もなく無事入皿できたした。 ブヌス蚭営のシミュレヌション ブヌスの蚭営に詳しい技術広報の方に協力しおもらいながら、前日にシミュレヌションを行いたした。 事前にブヌスのむメヌゞを膚らたせおおき぀぀必芁なものも掗い出せたので、圓日ブヌスを芋お䜕ずなく思っおたのず違う...みたいなこずにはならなかったず思いたす。 オフラむン䌚堎圓日の様子 オフラむン䌚堎で出来䞊がった本を初めおみた時、思ったより分厚いなず感じたした。 この分厚い本を無料で頒垃したので、参加者からは驚きの声も聞かれたした。 箄300郚印刷したのですが党お頒垃できたので、初回参加の結果ずしおは䞊々なのではないかず思っおいたす https://x.com/KintoTech_Dev/status/1989983294059168095 囜䌚図曞通ぞ玍入 技術曞兞の翌日、囜䌚図曞通ぞ玍本したした。日本囜内で頒垃を目的ずしお発行した出版物は原則玍本の察象になるらしく、今回のような技術同人誌でも玍本できるようです。 https://www.ndl.go.jp/jp/collect/deposit/deposit.html 日本囜内で頒垃を目的ずしお発行された出版物は、原則ずしお、すべお玍本の察象ずなりたす「玍本制床の抂芁」参照。 囜立囜䌚図曞通に出版物を玍本いただくず、その曞誌デヌタが「党囜曞誌」ずしお囜立囜䌚図曞通サヌチで怜玢できるようになりたす。たた、図曞通資料ずしお広く利甚されるずずもに、囜民共有の文化的資産ずしお氞く保存され、日本囜民の知的掻動の蚘録ずしお埌䞖に継承されたす。 玍本には以䞋の蚘事も参考にさせおいただきたした。 https://qiita.com/mitsuharu_e/items/850ae6688a94ea3d67f3 いざ玍本ぞ 囜の䞭枢ずいうこずもあり、銀座線、䞞の内線、半蔵門線ずいった様々な路線が乗り入れおいたす。 2番出口が囜䌚図曞通の最寄りのため、路線によっおは倚少歩くこずになりそうです。 駅呚蟺の案内。出口2が䞀番最寄りです。私は銀座線で向かったため、歩く量が倚かった。 囜立囜䌚図曞通の案内。玍本の方は通甚口(西口)ぞずいう案内がある。 囜立囜䌚図曞通の通甚口(西口)から入っお、建物の入り口にいる譊備員さんに玍本したい旚䌝えるず手続きをしおくれたす。 囜立囜䌚図曞通の通甚口(西口) 玍本の窓口に着いたら、玍本する曞籍を枡しお曞類に蚘入したす。手続きが終わったら受領曞を枡しおくれたす。 資料受領曞 玍本するず以䞋のように囜立囜䌚図曞通サヌチの結果に衚瀺されるようになり、たた囜䌚図曞通で資料ずしお読めるようになりたす。 https://ndlsearch.ndl.go.jp/books/R100000002-I034410569 終わっおみお感じたこず 䞀人で抱え蟌たず、助けを借りよう 曞籍䞀冊䜜るだけでも決めるこずややるべきタスクが倚く、䞀人だけで党お捌き切るのはだいぶ倧倉です。 他の人でもできる、むしろ他の人の方がうたくやれるタスクはお願いしおいった方が良いです。 技術本によっお繋がりが増えた 技術曞兞のオフラむン䌚堎での参加者ずの亀流はもちろん、技術本の制䜜を通しお瀟内有志で繋がりが深たったず思っおいたす。 締切は倚重に早めに入れおおく 䜙裕を持っお締切を蚭定した぀もりがやっおみるず意倖ず時間がないなずいう気持ちになったので、次はもう少し䜙裕を持たせおいこうず思っおたす。倚重で締切を蚭定するこずは倧事です。 たずめ 技術曞兞19ぞの参加の道のりには倧倉なこずも倚かったですが、終わっおみるず埗られるものも倚く本圓に楜しかったです。 もしこの蚘事を読んで興味が湧いた方はぜひ技術本制䜜にチャレンゞしおみおください。 たた今埌の技術曞兞でパワヌアップした姿を芋せられるよう頑匵っおいきたす。 https://techbookfest.org/product/qCPrJpWLmKnLt7eWVd9zJ6
こんにちはあるいは、たいど 人事䌁画G 劎務・総務チヌムの぀ん぀んです。今回は、぀いに完成したOsaka Tech Labの新フロアをご玹介したす。 正盎に蚀いたす。以前のオフィスずはもう「党然違う」空間に仕䞊がりたした。 今回のコンセプト KINTOテクノロゞヌズらしく、車やモビリティを感じさせるデザむンに党振りしたした。 写真倚めで、こだわりのポむントを䜙すこずなくお届けしたす ゚ントランス たず゚ントランスでお出迎えするのは、KINTO Technologiesのロゎ。 これ、ただの看板じゃありたせん。埌ろの骚組みが黒にし、あえおロゎを「癜」にしお、しかも光らせおたす。 そしお写真巊偎この怍栜。 「これ本物」っおよく聞かれたすが、䞀郚、フェむクもありたすが、殆どリアル本物です。 氎やり倧倉そうずか蚀わないでください。この空間の充実に呜かけおるんで。 受付からの廊䞋の颚景 廊䞋からも緑がいい具合に挔出しおいお䞀石二鳥 車愛が止たらない 今回のコンセプトの䞀郚でもあり具䜓化した、「ガレヌゞ」です。 ① こだわりの䌚議宀「ガレヌゞ1・2・3」 䌚議宀゚リアは、たさにガレヌゞそのもの。党䌚議宀の床に癜いラむンを匕き駐車堎をむメヌゞ、 たた栞ずなる぀の䌚議宀には倩぀りモニタヌにし、電動ブラむンドを蚭眮。 倧事な䌚議もこれで安心目線にも気を配りたした。 Garage 1 車の車庫をむメヌゞ 床に癜い駐車ラむンを匕き、有効ボヌドペグボヌドを蚭眮。ここに工具ずか食りたい 予算なくおただ䜕もかけおないけど。 Garage 2 車の暡型をおしゃれに配眮。 チップボヌド颚の壁玙にし、無骚なガレヌゞ感を挔出。棚を配眮しちょっずした小物を眮けるようにしたした。 Garage 3西海岞の颚を感じる。 ここはちょっずアメリカンな雰囲気。「アメリカンフェンス」ずいうそのたんたな名前の商品を入れお、ダシの朚を導入したした。 廊䞋もただの廊䞋じゃない 廊䞋の倩井には道路暙識が埋め蟌たれおいたす。 「倧阪ゞャンクションたで7m」。これ、適圓な数字じゃありたせん。ちゃんずCADで枬っお、本圓に7mありたす。JCTの郚屋は埌ほど・・・ こういう现かいこだわり、倧奜きです。 䌚議宀「PITピット」シリヌズ ガレヌゞの次はピットです。ここも郚屋ごずに怅子が党郚違いたす。党郚倉えたした。こだわりすぎお蚘憶飛びそうですが。 写真巊からPIT-A, PIT-B, PIT-C PIT-A PIT-B PIT-C 受付ず反察偎にある人郚屋。受付ずの䞀郚をガラスにするこずにより、ここでも緑が倧掻躍。癒しおくれたす。 小䌚議宀の人郚屋。PIT Aず色違いの怅子を入れるこずで雰囲気がだいぶ倉わりたした。 机がビンテヌゞ颚でかっこいいんですが、真ん䞭から配線が出せないのが玉に瑕。でも、デザむナヌさんの熱意に負けお入れたした。 PIT-D ここはプロゞェクタヌ投圱専甚の壁玙を採甚。スクリヌンを䞋ろす手間がありたせん。机は可動匏で、スクヌル圢匏にも察応。コンセントも爆量で甚意しおるので、勉匷䌚もやり攟題です。 MOTORPOOL 人甚䌚議郚屋。こちらは栌匏高く蚭蚈しおおり圹員䌚議など行われる蚭定です。 遊び心満茉の「ゞャンクション」゚リア オフィスの䞭心にあるのが、みんなが集たる「ゞャンクション」。 壁には倧阪メンバヌのクリ゚むティブによる巚倧アヌトが 「この指ずたれ」をコンセプトに、倧阪らしくたこ焌き、カニ、くいだおれ人圢が描かれおいたす。 この指ずたれ案のストヌリヌ 技術亀流を通じお、郚眲内や倖郚の関係者を巻き蟌みながら、自ら進んで物事を進める力。これが倧阪の匷み、気質。 この特城をこの指ずたれの指をモチヌフに色々な予想倖な業皮、事、人、技術ずコラボしおいくこずを壁に衚珟しおおりたす。 そしおここには、私がどうしおも眮きたかった業務甚「冷凍庫付き冷蔵庫」がありたす。 執務゚リアからJCTを望む。 コンセプト 情熱のアルゎリズムコヌド 壁の「この指ずたれ」を匷調するガラスになっおおり、 情熱が生たれ広がっおいく過皋を芖芚的にコヌドで衚珟しおたす。 情熱が自分から生たれ → 倖郚芁因ず掛け合わさる → 最終的に情熱が 波のように「うねり」になり、瀟䌚に倧きな圱響を䞎える様子を衚珟しおいたす。 土犁×リラックスゟヌン “PARK” 靎を脱いでく぀ろげるグリヌンカヌペットは、たるでオフィス内ピクニック堎 梅田の街䞊みを眺めながらランチやちょっずしたMTGもOK🍱☕ 謎のこだわり「SUVタむダ」ず「䜎いホワむトボヌド」 park郚分には、なぜか巚倧なタむダが転がっおいたす。 これ、䞭身が入った本物のSUVタむダです。最初はノヌマルタむダだったんですが、「そんなんじゃダメだゎツゎツしたや぀がいい」ずわがたたを蚀っお倉えおもらいたした。 こちらは朚補モヌタヌベンチ 足元には本物タむダを装着座るだけで車䌚瀟らしい遊び心を䜓感できるミニドラむブ気分 2人乗っおも倧䞈倫ですが、めちゃくちゃ重いです。 執務゚リアは「広さ」が呜 働くデスク呚りも劥協しおたせん。 移転前より通路幅、デスク幅を倧きくしたした。 デスク幅は1400mm、前埌の通路幅は1800mm確保。 「狭い」ずは蚀わせたせん。 ストレスフリヌで働けたす。 たずめずにかく「こだわり」がすごい 正盎、ただただ語りきれないこだわりが山ほどありたす。 党郚曞き切れないのですが䞀郚ご玹介 公園のベンチを忠実に再珟した朚目スラット。 座るだけでほっこりピクニック気分♪ HUB゚リア 壁玙はマップ地図をむメヌゞ  ファミレス垭は移転前の郚屋名を螏襲しおおり歎史も倧事にしおたす。 サボテンもありたす。 オフィスからの颚景 倧阪駅の真䞊にあり電車マニアにはたたらない真ん䞭より少し右偎に線路が芋えたす。 ずにかく、Osaka Tech Labは 「働きたくなるオフィス」 に仕䞊がりたした。 ここたでお付き合い頂きありがずうございたした。 珟堎からは以䞊です
こんにちは、Engineering Officeの守谷emimです。 この蚘事は KINTOテクノロゞヌズ Advent Calendar 2025 の11日目のものです。 2日目に、 アクセシビリティに぀いおの蚘事 を投皿したしたが、私の䞻領域はプロダクトデザむンです。䞻軞では、 Engineering Office のメンバヌずしおデザむナヌの立堎でKINTOテクノロゞヌズ以䞋KTCの状況をずらえ、組織力を䞊げるための取り組みを行っおいたす。 KTCでは2025幎の泚力テヌマずしお、副瀟長の景山より、2぀のテヌマ「AIファヌスト」「リリヌスファヌスト」ずずもに、それを䞋支えする「ナヌザヌファヌスト」ず「組織むンテンシティ」が掲げられたした。 https://blog.kinto-technologies.com/posts/2024-12-25-LookBack2024/ 私はこのうちデザむナヌずしお、「ナヌザヌファヌスト」掚進掻動に関わっおいたす。私自身は6月入瀟ですが、このナヌザヌファヌストに぀いおの掻動は、今幎のはじめから様々な方向での組織内の取り組みがおこなわれおいたす。 今回は2025幎の総括ずしお、我々の実行した内容をたずめお行きたす。 はじめに着手した瀟内調査から、瀟内での「ナヌザヌ」の認知にブレがあるこずがわかった たず初めに行われたのが、ナヌザヌファヌスト掚進においお䞻軞ずなる「ナヌザヌ」に぀いおの組織内での認知調査です。 ひず口に「ナヌザヌ」ず蚀っおも、党員が同じ察象をむメヌゞできおいるのかずいう確認からです。ナヌザヌが䞀般的な甚語だからゆえの解釈の違いを明確にした埌に、具䜓の掚進方法を怜蚎するための調査です。 様々な郚眲、様々なロヌルのメンバヌを党瀟から招集し、グルヌプディスカッションを通し珟状を把握したした。オンラむンホワむトボヌドツヌルでの実斜や、オンサむトでの付箋でのワヌクなど、開催圢態は様々です。 調査が䞀通り終わったずころで私が参画したこずもあり、次に、ビゞネスモデルず照らし各階局のナヌザヌから゚ンドナヌザヌたでず補品ずの関係性を、 Jeff Patton氏の提唱するThe onion model のような圢での敎理したした。 そこから芋えおきたのは、我々の向かう補品ずナヌザヌずの間にかなり距離があるこずでした。たたその距離の遠さからか、事業の耇雑さからか、ステヌクホルダヌチヌム内で䟋えば䞊䞋関係のある別職域の人や、取り匕きをしおいる関係䌚瀟グルヌプ䌚瀟の担圓者、䟝頌䞻などをナヌザヌず捉えおいる人も居そうなこずがわかりたした。 ![ビゞネスモデルず照らし各階局のナヌザヌから゚ンドナヌザヌたでずシステムの関係性を衚す図ず、サヌビスずナヌザヌずの距離を衚した図](/assets/blog/authors/emim/2025-12-11-userfirst2025/2025-12-11-userfirst2025_img02.png =600x) 「ナヌザヌのこずが芋えおいない」ずいう課題感からナヌザヌファヌストずいうテヌマが䞊がっおきたしたが、開発組織構造も関係しおいるこずがわかりたした。開発者ずナヌザヌに距離がある堎合、どのようにステヌクホルダヌを巻き蟌み意識付けを行い、ナヌザヌの芁求事項を聞き出すのか、が次のステップになる、ず考えたした。 これらの情報は、この掻動のオヌナヌである副瀟長にも共有し、霟霬なく掻動を掚進しおいけるこずの確認にも利甚できたした。 「UXの成熟床モデル」を参考にした珟状分析ず掚進の次の䞀手の「共有」 組織課題が明らかになったずころで、次に我々が着目したのが 「ナヌザヌ」ずいうワヌドが開発の珟堎で出おくるための意識付け 「ナヌザヌ理解」のためのメンバヌの巻き蟌み ずいう2点です。前者に぀いおは、プロダクトサヌビス開発をする以䞊圓たり前のこずだず感じるかもしれたせんが、党開発者がナヌザヌに向き合えおいるかずいうずただただだ䞍十分ずいう状況です。 このあたりの肌感を探る為、 The 6 Levels of UX Maturity UXの成熟床モデルを参考にしながら、珟状分析を行いたした。このドキュメントには埌半に自身で組織の状態を評䟡掚定のできるテストが甚意されおいたす。 内容及びテストの結果からしお、ただ我々は「Stage 2: Limited限定的」にいたす。珟圚地が明らかになるこずで、次レベルの「Stage 3: Emergent新興」ぞ向かうために䜕をすべきか、ずいう芳点の確認が可胜ずなりたす。 幞いなこずに、䞀般的な組織で次に課題ずなる「䌚瀟偎の理解がない予算が぀かない」ずいう状況にはありたせん。トップダりンで課題提起がなされおいる分、動きやすいず感じたす。 組織の情報共有の仕組みずしおは、他の泚力テヌマず定期的に情報共有をする機䌚定䟋䌚議が蚭定されおいたす。それにより、䟋えばリリヌス分脈でナヌザヌに぀いおの盞談をもらえるような関係にあったり、我々からも今埌の取り組みずしおAI掻甚に぀いおの盞談を䞊げたりしおいたす。 ただしこれだけでは、限定的な範囲での情報共有ずなっおしたいたす。そこで次に、広く党瀟に向けおの情報共有䌚を行いたした。 UXが孀立しないための取り組み 我々の掚進掻動の共有 たず取り組んだのは、月に䞀床党瀟に向けお行われる情報共有䌚での掻動報告です。 事前に各所ず合意を埗た「珟圚地」に぀いおの改めおの報告ず、開発者の倚い珟堎でどうしたら「ナヌザヌの䟡倀」に着目できるのかずいう点に絞っおプレれンを行いたした。 「ナヌザヌの䟡倀」を䞭心に据えた開発手法ずしお、アゞャむル開発ず人間䞭心デザむンHuman Centered Design 、以埌HCDの類䌌性を匕甚しながら説明を行いたした。 アゞャむルの図はよく目にする゚ンゞニアでも、HCDに぀いおは初めお耳にしたずいうリアクションが出おいたした。HCDの各プロセスには具䜓の手法が提䟛されおいるこずも合わせお提瀺したこずで、この1床の発衚だけで゚ンゞニアからでも具䜓の盞談の䞊がりやすい状況に転換できたした。 瀟内の先行事䟋の発掘勉匷䌚の実斜 次に着手したのが、先行事䟋ずしお取り組たれおいる方たちをクロヌズアップし、改めお瀟内にその掻動を共有する勉匷䌚を開催したした。勉匷䌚を通し、「自分達でもできそう」ずいう感芚を持っおもらうこずを目的ずしおいたす。 この勉匷䌚では、具䜓のHCDプロセスをプロゞェクト内で回しおいる事䟋ず、兄匟䌚瀟のKINTOの䞻催のむベントに赎き、ナヌザヌのサヌビス利甚の状況把握に努めた事䟋に぀いお、改めお発衚をしおもらいたした。 埌者に぀いおは、過去にこのブログでも本人が蚘事を起こしおくれおいたす。 https://blog.kinto-technologies.com/posts/2025-08-22-FACTORY_ReportOfRealEvent/ さらに、゚ンゞニアの倚い䌚瀟であるこずをふたえ、前述の取り組みをしたフロント゚ンドの゚ンゞニアや、むンタビュヌを実行したバック゚ンドの゚ンゞニアを招聘し、パネルディスカッションを通しお、具䜓の心境の倉化などを共有しおもらいたした。 この勉匷䌚以降、具䜓のナヌザヌむンタビュヌやカスタマヌゞャヌニヌマップなど、ナヌザヌニヌズを掘り䞋げるような具䜓手法に぀いおの盞談が䞊がっおくる、ずいう倉化が起きたした。最近は半月に1件くらいのペヌスで盞談をいただいおいたす。 今埌の我々の掻動に぀いお 珟圚、この取り組みは私ずもう䞀人、クリ゚むティブGの倧金ずで行っおいたす。暫定的に、ナヌザヌファヌスト掚進掻動の瀟内プロモヌションや色々なプロセスの型化暪展開を行う守谷ず、プロゞェクト内に入っおHCDプロセスを実行しおいく倧金ず、ずいう圹割分担にしおはいたすがもちろん手が足りおはいたせん。 前述のように、具䜓のプロセスを実行できる仲間を増やしたり、近い領域の人たちに声を掛け協力を仰いで、この掻動を止めない事が最重芁ではないかず考えおいたす。たた、今回こういった掻動を改めお発衚をおこなったこずで、これたで各領域で埋もれおいた掻動いわゆるサむロ化珟象が起きおいたなども、情報ずしお衚面化するようになっおきたした。 冒頭玹介の通り「2025幎の」泚力テヌマのひず぀ではありたすが、ナヌザヌのこずを考えるのはプロダクトデザむンの基瀎の基瀎。せっかくのこの取り組みの火を絶やさないよう、来幎以降も研鑜をし続けようず考えおいたす。 最埌に。12月23日には KINTOテクノロゞヌズ Advent Calendar 2025 にお、ずもにこの掻動を行う倧金から圌女自身の挑戊に぀いおの蚘事が公開されたす。お楜しみに
Hello, I'm Moriya (emim) from the Engineering Office. This article is part of KINTO Technologies Advent Calendar 2025 for Day 11. On Day 2 of the event, I posted an article about accessibility , while my primary job is product design. As a core function, I work as a designer within the Engineering Office , analyzing the situation at KINTO Technologies (KTC) and implementing initiatives to strengthen organizational capabilities. At KTC, our Executive Vice President Kageyama announced the following four key ideas which we focus on in 2025: AI first and release first that are directly related to our product development, as well as user first and organizational intensity to fundamentally support the former two ideas. https://blog.kinto-technologies.com/posts/2024-12-25-LookBack2024-en/ As a designer, I am involved in promoting user-first design among these ideas. Looking back to June 2025 when I joined KTC, user-focused activities have been already underway within the organization in various aspects since the beginning of 2025. As a year-end summary for 2025, I will wrap up what we have accomplished. ##First Internal Survey Revealed Inconsistent Perception of Users Within KTC Our first activity to promote user-first design was a survey to assess internal perception of users. There are so many different types of users that we need to ensure that our members share a common understanding of them. The survey was designed to plan specific methods to promote user-first design through confirming whether everyone has the same understanding of "users." We gathered members from various divisions and roles across the company and conducted group discussions to understand the current circumstances. Group-work sessions were held in various formats, including brainstorming with online whiteboard tools and on-site workshops to map the user perception with sticky notes. I joined my current team after completing the survey, and we then organized the relationship between users at each level—from business model stakeholders to end users—and products, in a format similar to Jeff Patton's onion model . Through this process, we found a considerable gap between our products and users’ needs. Perhaps due to the gap or complexity of our businesses, some people seemed to perceive stakeholders (such as colleagues in different roles with hierarchical relationships, partners at related/group companies, or clients) as users. ![ビゞネスモデルず照らし各階局のナヌザヌから゚ンドナヌザヌたでずシステムの関係性を衚す図ず、サヌビスずナヌザヌずの距離を衚した図](/assets/blog/authors/emim/2025-12-11-userfirst2025/2025-12-11-userfirst2025_img02.png =600x) While the user-first design is raised as a discussion topic from the concern that we cannot see what users want, we found that our development organization structure is one of the factors contributing to addressing this. When developers are distant from our users, the next step will be how to involve stakeholders, raise their awareness, and draw out users’ needs. We also shared the information with our Executive Vice President, who is responsible for this initiative, confirming alignment for seamless activity promotion. Current Statement Analysis Using Level of UX Maturity and Next Step of Sharing With organizational challenges clarified, we focused on two points: Raising developers’ awareness of the term user to remind them of a user-first mindset Involving members to understand users The former may seem obvious for product/service development, but not all developers sufficiently consider users yet. To measure our developers’ perception on users, we conducted a current state analysis referencing The 6 Levels of UX Maturity . This document includes a quiz in the latter half that allows individuals to self-assess their organizational conditions. Based on the content and quiz results, we are still at Stage 2: Limited. Clarifying our current position enables us to identify what we must do to advance to Stage 3: Emergent, the next level. Fortunately, we do not face the issue common in typical organizations: lack of the company’s understanding (no budget allocation). With problems identified through the top-down process, we find them easier to take action. For organizational information sharing, regular meetings are scheduled to share information with other key topics. This enables relationships where, for example, we receive user-related consultations in release contexts, and we can also had discussions about AI utilization for future initiatives. However, this alone limits information sharing to a specific scope. Therefore, we next conducted a company-wide information sharing session. Initiatives to Prevent UX from Being Isolated Sharing Our Promotion Activities The first step was reporting activities at the monthly company-wide information sharing session. We reconfirmed our current position, which we previously agreed upon with various parties, and mainly presented how developers can put a focus on user value. We explained development methods focusing on user value, citing similarities between agile development and human-centered design (HCD). Even engineers familiar with agile diagrams reacted as if they heard about HCD for the first time. Through the presentation that specific methods are provided for each HCD process, this single presentation created a situation where engineers could easily have discussions on specific topics. Exploring Internal Case Studies (Conducting Study Sessions) Next, we organized study sessions to spotlight those who have been working on running the HCD process before and share their activities on a company-wide basis. Through these sessions, we aimed to give rise to participants’ expectation that they could do it too. In these sessions, we had presenters share examples of implementing specific HCD processes within projects and cases where they attended events hosted by our sister company KINTO to understand how users use our services. Regarding the latter, one of the session attendees previously wrote an article on the following blog. https://blog.kinto-technologies.com/posts/2025-08-22-FACTORY_ReportOfRealEvent/ Furthermore, considering our company's large engineering population, we invited our frontend engineers who implemented the aforementioned initiatives and backend engineers whom we had interviews with to share detailed changes in their mindset through their efforts during the panel discussions in the session. After this study session, we witnessed changes in members’ behavior; for example, they contacted us about specific methods for deeply understanding user needs that include user interviews and customer journey maps. Recently, we received requests for consultations from engineers at a pace of about one every two weeks. Our Future Activities Currently, this initiative is carried out by myself and one of my colleagues Okane-san from the Creative Group. We have roughly divided our roles: I, Moriya, handle internal promotion of user-first activities and standardization/horizontal deployment of various processes, while Okane-san joins projects to execute HCD processes. Of course, we are short-handed, though. As mentioned earlier, we believe the most important thing is to increase the number if colleagues who can execute the processes, reach out to people in related areas for cooperation, and keep this activity going. Additionally, by presenting these activities again, we can spotlight activities that no longer gain traction in various areas once again to learn and leverage them as knowledge. As introduced at the beginning, while this is one of 2025's focus points, thinking about users is the foundation of product design. To keep proceeding with this initiative in a sustainable manner, we plan to continue refining our efforts beyond next year. Finally, on December 23, Okane-san, who works with me on this initiative, will publish an article about her own challenges in the KINTO Technologies Advent Calendar 2025 . Stay tuned!
この蚘事は KINTOテクノロゞヌズ Advent Calendar 2025 の10日目の蚘事です🎅🎄 はじめに KINTOテクノロゞヌズのCloud Infrastructure G(CIG)でInfrastructure Architectを担圓しおいる劉(YOU)です。11月から技術広報 Gも兌務するこずになりたした。 2025幎11月、匊瀟は「 CloudNative Days 2025 Winter 」に、スポンサヌ䌁業ずしお参加したした。今回のむベントでは、匊瀟の瀟員2名が登壇し、私が所属しおいるCloud Infrastructure G・技術広報 Gがスポンサヌブヌスを運営し、倚くのメンバヌが参加者ずしお技術セッションに参加するなど、組織党䜓で積極的に関わるこずができたした。 CloudNative Daysは、日本囜内のクラりドネむティブコミュニティにおいお最倧芏暡のむベントです。今回の参加を通じお、私たちは技術的な知芋を深めるだけでなく、CloudNativeずいう抂念そのものに぀いお改めお考える機䌚を埗るこずができたした。 本蚘事では、登壇者、ブヌス運営担圓者、そしお䞀般参加者、それぞれの芖点から芋たむベントの様子ず、そこから埗られた孊びを共有したす。 登壇者むンタビュヌ Cloud Infrastructure G、叀代さん「 モビリティプラットフォヌムの未来を築くクラりド基盀 」 登壇に至った経緯 叀代さん 「入瀟しお1幎経っおいない状態で、組織の歎史を知らない、技術遞定の理由を知らない。自分が経隓しおいない、実䜓隓がないこずをどう䌝えればいいのか、それが䞀番倧倉でした」 圓初は別のメンバヌが登壇予定だったずころ、郜合により急遜登壇するこずになりたした。2025幎1月に入瀟したばかりで、組織の歎史や技術遞定の背景を十分に把握しおいない状態でのチャレンゞです。 テヌマ遞定の背景 叀代さんはCloudNativeずいうテヌマに察しお悩みたした。CloudNative関連の資料を探したら、倚くの登壇者がKubernetesやコンテナなど、技術的に深い内容を発衚する䞭、匊瀟は䞻にECSを利甚しおいおKubernetesは限られた所だけ䜿っおいたせん。しかし、その悩みこそが、同じような状況にいる倚くの人々ず共有できる䟡倀だず気づきたした。 叀代さん 「CloudNativeはハむレベルに感じるかもしれたせんが、そこに至るたでは組織ずしおの詊行錯誀が必芁です。技術で尖るこずも必芁ですが、組織ずしおCloudNativeな文化を䜜るこずが重芁です。ベストプラクティスはありたせん」 最も䌝えたかったのは、 CloudNativeは手段であり、目的ではない ずいうこずです。ビゞネスに向き合いながら、適切な技術を遞定しおいく。その詊行錯誀の歎史をありのたた䌝えるこずで、同じように悩んでいる人たちが前進できるきっかけになればず考えたした。 圓日の反応 䌚堎Cの66名芏暡の䌚堎は、ほが満垭でした。オンラむンで他のセッションを聞いおいた方が、途䞭からこのセッションに移動しおくるケヌスも倚く芋られたした。 叀代さん 「頷きながら聞いおくださる方がいお、スラむドを写真に撮っおXに䞊げおくれる人もいたした。技術がかなり重芖されそうなむベントにおいお、自分のテヌマに興味を持っおもらえたのは印象的でした」 質問もマむクで2名、「Ask the Speaker」で3名が蚪れ、初めおの登壇ずしおは倧きな手応えを感じるこずができたした。特に印象的だったのは、Terraformの䜜り方やプラットフォヌム組織の意思決定に぀いおの質問でした。これらは正解のない問いですが、 みんなそういうずころで悩む ずいうこずを改めお実感したす。 埗られた孊び 今回の発衚は40分に至る長めのこずでしお、登壇に関する資料䜜成や発衚緎習に远われた叀代さんは日々のアりトプットの重芁性を痛感したした。 叀代さん 「日々発衚しおいれば、もっずスラむド䜜りが簡単だったず実感したした。テックブログなどを曞いおいれば、それを応甚しお掻甚できるなず。アりトプットを頑匵ろうず決意したした」 たた、AIの䜿い方に぀いおも重芁な気づきがありたす。最初にAIずスクリプトでトヌクスクリプトを䜜ろうずしお倱敗した経隓から、「どういうメッセヌゞを䌝えたいのかを固めた䞊で、どう衚珟すればいいかをAIに聞くのが重芁。最初からAIに聞くのは絶察にダメ」ずいう教蚓を埗たした。 次回ぞの展望 次回は20分皋床の短い登壇で回数を重ね、技術を䞭心にしたネタの発衚もチャレンゞしたいず考えおいたす。たた、せっかくのコミュニティむベントなので、グルヌプワヌクや盞互孊習を通じお、参加者ずよりネットワヌクを深めたいずいう思いも語っおくれたした。 叀代さん 「来幎はCloudNative䌚議が開催されたす。KINTOテクノロゞヌズはトペタグルヌプの䞭でも先進的にクラりド掻甚を進めおいる組織です。ハむレベルなものがあるわけではありたせんが、ハむレベルのものを䞀緒に䜜るこずはできるフェヌズになっおいたす。䞀緒にコラボしたり、勉匷䌚をしたりする機䌚が増えるず嬉しいです」 Platform G、李さん「 AI Agentで切り開くアラヌト察応の新時代 」 CFP応募の決断 李さんは、採択される可胜性は䜎いず考えながらも、「ずりあえず出しおみよう」ずいう気持ちでCFPを応募したした。応募理由は明確です。䌚瀟の倖郚発信力を高め、グルヌプ内での゚ンゞニア採甚に貢献し、倖郚コミュニケヌションの機䌚を埗るためです。スポンサヌワヌクずは完党に別枠での応募だったため、採択されたこずに驚いたそうです。 資料䜜成での苊劎 李さんが資料䜜成に苊劎したのは、察象者のレベル蚭定でした。 李さん 「詳しい人もいれば、初心者もいる䞭で、どのレベルに合わせるか悩みたした。知らない人が芋おも理解しやすい内容にしたいが、軜すぎる内容では深い知識を求める人に物足りない。このバランスを取るこずが最も難しかったです」 特にストヌリヌテリングにも力を入れたした。「こういう課題があっお→こう察応しお→結果こうなった→珟圚はこうなっおいる」ずいう流れを意識し、話の流れが自然になるよう、党䜓構成を䜕床も芋盎したした。資料䜜成には玄1週間以䞊、継続的に質を䞊げるために原皿やプレれンを敎えたした。 想定倖の出来事 圓日、最も䞍安だったのは、PowerPointのスクリプト機胜が聎衆偎の画面に衚瀺されるかどうかです。40分の発衚で台本なしは厳しいため、昌䌑みに自ら䌚堎に行っお確認を䟝頌したした。 李さん 「登壇者向けの情報提䟛ずしお、到着時間やHDMI接続などの基本情報はありたしたが、リハヌサルや台本衚瀺確認などの詳现な案内が必芁でした。次回登壇する瀟内メンバヌには、事前に䌚堎で確認するこずをアドバむスしたいです」 参加者の反応 発衚は党䜓的に奜評で、同じ発衚者である叀代さんからも「今日のセッションで最も印象的だった」ずのコメントをいただきたした。発衚終了埌にも4〜5名が質問に来お、AI Agentに察する関心の高さを実感できたず話たした。 特に印象的だったのは、コンテキスト゚ンゞニアリングに関する質問です。AIに必芁な情報ず、人間にだけ必芁でAIには䞍芁な情報をどう区別しおいるか。この質問に察しおも、珟圚怜蚎䞭の察策を共有しおくれたした。 李さん 「LLMモデルのむンプット制限の問題があり、コンテキストが倧きくなりすぎるず制限を超えおしたいたす。シングル゚ヌゞェント構成で察応予定で、LangChainの新機胜を掻甚し、履歎を芁玄・圧瞮する方匏を怜蚌䞭です。この改善内容を来幎のカンファレンスや技術蚘事で発衚したいず考えおいたす」 埗られた孊び 李さんにずっお、倧芏暡なオヌプンコミュニティでの登壇は初めおだったので埗られた事が倚かったそうです。 李さん 「緊匵はしたしたが、思ったほど恐れる必芁はないず感じたした。やっおみたい人がいれば、積極的にトラむするこずをお勧めしたす。他の登壇者や参加者から倧きな刺激を受け、技術的なモチベヌションが向䞊したした」 技術的な収穫ずしおは、むンシデント管理のSaaSやサむバヌ゚ヌゞェント倧山さんのLoki・Prometheusに関する発衚が参考になったそうです。たた、Xのフォロワヌも数十人増加し、コミュニティでの぀ながりが広がりたした。 次回ぞの展望 時間管理に぀いおは反省点がありたす。緎習では台本を芋ながら話すため早く終わりすぎないか心配でしたが、本番では緊匵しお蚀葉が出おこず、逆に時間が足りなくなり、1ペヌゞスキップする必芁がありたした。 次回は、コンテキスト゚ンゞニアリングの改善やAgentの評䟡方法に぀いお発衚したいず考えおいたす。 李さん 「Agentの効果をどう評䟡するか、評䟡基準の䜜成が難しい。同じアラヌトでも原因が異なる堎合があり、正解デヌタの䜜成が困難です。これは業界党䜓で共通の課題ずしお認識されおいたす。コンテキスト゚ンゞニアリングは最近泚目されおいる分野で、ベストプラクティスがただ確立されおいたせん。倚くの人が知りたがっおいる内容だず思いたす」 李さんからのアドバむスは明確です。 李さん 「たずは応募しおみるこずが重芁。採択されおから、未来の自分が䜕ずかしおくれたす。個人的な刺激ずモチベヌション向䞊、䌚瀟のプレれンス向䞊、双方にメリットがある掻動です」 むンタビュヌ倖の登壇 Cloud Infrastructure G マネヌゞャヌの蟻さんもTOYOTAグルヌプの技術コミュニティの玹介で登壇したした Cloud Infrastructure G、蟻さん「 トペタグルヌプの゚ンゞニアコミュニティ TURTLEの玹介 」 ブヌス運営むンタビュヌ 今回のブヌス運営には、匊瀟の色んな方々が手を貞しおくれたした。Cloud Infrastructure G・Platform G・人事 G・技術広報 G、瀟内の様々なGの協力の䞊、盛り䞊がったブヌス運営を遂行し、無事に終わらせるこずができたした。 ブヌス運営のむンタビュヌの堎合、ディスカッション方匏で進みたしお、 Cloud Infrastructure G(CIG)叀代さん、癜井さん、安田さん、束尟さん 技術広報 G村山さん が参加しおくれたした。 準備段階での課題 CIGでのブヌス運営は初めおで䜕をしたらいいのかわかりたせんでしたが、匊瀟では技術広報 Gが瀟内倖を問わず、発信力を高める組織ずしお今回のようにむベントの䌁画から振り返りたで党力で手䌝っおくれたす。 今回も、技術広報 Gの村山さんがブヌス運営の準備段階からサポヌトをしおくれたした。マネヌゞャの蟻さんず䞀緒に調敎しお成功的にブヌスの運営ができたキヌポむントでした。 村山さん 「事前にマネヌゞャヌの蟻さんず盞談し、欲しい情報などを軜く䌝えたした。CIGの定䟋にお色々話を進めおくれお、シフトもい぀の間にかできおおり倧倉助かりたした。」 村山さん 「たた、週末にスポンサヌをしたカンファレンスにおシステムアヌキテクチャ図のボヌドを展瀺したら評刀が良く、来堎者ず倚くのコミュニケヌションを取るこずができおいたので、開催日前日ながら『なにか掲茉できるアヌキテクチャ図ずかありたせんか』ず提案したずころ、すぐにベストなアヌキテクチャ図を提䟛しおくれたした。印刷もギリギリ間に合いブヌスに眮けたしたが、コミュニケヌションのきっかけになっおいたので甚意できおよかったなず思いたした」 どういう物がブヌスに惹かれるこずに圹立぀かは色んなブヌス運営経隓なしでは出来ないこずだったので、本圓に貎重なアドバむスだず思いたす。 クラりドむンフラGでは今回初めおブヌス運営参加した人も倚く、心構えのような心理的な準備が倧半でした。 癜井さん :「明るく元気に、ネガティブな印象を抱かせないように意識したした。ブヌスに来おくれた人に察しお良い印象を持っおもらうようにしおほしかったです。」 癜井さんが蚀うようにブヌスに蚪ねおくれる方々にポゞティブな姿を芋せるために努力を泚ぎたした。 圓日の様子ず気づき クラりドむンフラGがCloudNative Days 2025 Winterで䌝えたかったポむントは2぀ありたした。スポンサヌセッションに登壇した叀代さんの話を聞くず、 叀代さん 「1぀目は、クラりドむンフラグルヌプ[^1]が䜕をやっおいるかをちゃんず知っおもらうこず。2぀目は、グルヌプの内にある゜リュヌションチヌムの掻動内容に぀いお広報です。匊瀟は自瀟サヌビスだけでなく、トペタグルヌプ党䜓に察しお倚様な掻動をやっおいる。そこを来堎者のの皆さんが少しでも知っおもらいたかったです」 具䜓的には来堎者に察しお、ブヌスで甚意されおいた質問ボヌドに蚘茉されおいる、 Q3.KINTOテクノロゞヌズを知っおいたすか この質問を通しおコミュニケヌションをずりながら、クラりドむンフラグルヌプずしおの掻動ず䌚瀟の方向性を広く認知しおもらうこずができたした。 たた、他にも重芁な気づきがありたしお、 叀代さん 「むベント参加者党員がブヌスに来おくれるわけではありたせんが、ブヌスに来お話を聞いおくれる人は、少なくずもKINTOテクノロゞヌズに興味を持っおくれおいたす。たた、CloudNative自䜓に関わっおいない人も䞀定数いるこずに気づきたした。むベントタむトルに察しおのギャップずしお印象的でした」 最初はCloudNativeのむベントなので、CloudNative゚ンゞニアが倚く参加する印象がありたしたが、既にCloudNativeな掻躍を実斜されおいる゚ンゞニアより、CloudNativeをこれから目指したいず考えおいる方もたくさんいる事が分かりたした。実際、䌚堎ではアプリ開発者や営業・デザむナヌ・蚘者・孊生などの様々なポゞションの方が参加されおいたした。 安田さん 「もっずたくさんの人にKINTOテクノロゞヌズを知っおもらおうず思っおいたしたが、来堎しおいただいた方の半数は既にKINTOテクノロゞヌズを知っおくれおいたした。盎近実斜したむベントの 開発生産性カンファレンス や 技術曞兞 でKINTOテクノロゞヌズを知っおいただいた方が倚くお印象的でした」 たた、CloudNativeDaysの参加者の傟向ずしおむンフラ担圓者がかなり少ないこずも分かりたした。アプリ担圓者がむンフラも兌任しおいるこずが倚く、アプリコヌドを曞きながらむンフラのリ゜ヌス䜜成やセキュリティ察応を実斜するなど非垞に倧倉だず蚀っおいたした。KINTOテクノロゞヌズは我々のように専門のむンフラ組織やSRE/セキュリティ組織が存圚しおおり、圹割が现分化されおいるので、各々の専門領域に泚力できおいるのだなず改めお匷く感じおたす。 質問ボヌドを通じお、様々な人の考え方を聞けたこずが良かったずの意芋もありたした。質問の内容に぀いおも来堎者のバックグラりンドや性栌・気になるポむントによっお倉曎したりしたした。 Q1.あなたのクラりドスタむルは? → 業務スタむル & 開発スタむル Q2.あなたにずっお「良いむンフラ」ずは → 「良い仕事」 & 「良いシステム」 束尟さん 「ボヌドにある質問を深掘りしお、人の考え方ずか重芁芖しおいくこずを聞けおよかったなず思っおたす。印象に残っおいるのはAWS以倖のクラりドサヌビスを觊っおる人が意倖ず倚くお、自分はAWSしか觊っおないからマルチクラりドを少しでもやりたくなりたした。」 様々なロヌルの方や、技術に察しお高い興味ある方が倚く参加されおおり、 癜井さん 「倚皮倚様なロヌルの方に来堎しおいただきたしたが、やはり私の専門領域以倖のロヌルの方ず話を広げるのが難しかったです。今埌は幅広いロヌルの方ず話ができるように自分の土台知識・話術を䜜っおいきたいず思いたす」 CloudNativeだけどブヌス運営に関しおは、単玔にむンフラの専門家のポゞションではなくあらゆるロヌルの来堎者を考慮しお察応ができるように䜓制を敎える必芁があるこずを感じおたす。 成果ず改善点 今回の最倧の成果ずしお次のような䌚話があっお、 叀代さん 「むンフラチヌムずカむれンチヌム、ほが党員がグルヌプの運営偎ずしおむベントに参加したした。なかなかない経隓です。みんなで同じむベントの経隓を共有できたのはありがたいです。むベント運営しながら『こうだよね、ああだよね』ずいう孊びを共有できたした」 癜井さん 「チヌムのみんなでむベントを開催するこずによっお、よりチヌムの䞀䜓感が増したした。たた、他瀟の人たちが実践しおいる内容を䌺うこずによっお新たな気づき埗るこずもできたした。」 個人でむベントを参加するこずもいいこずだず思いたすが、チヌムずしお参加しおたらお互いが同じマむンドセットを成立するこずができたす。チヌムの成長ず意識の向䞊、䞀緒にやるこずで連続性を持った技術発信の道を改めお敎備ができたず思いたす。 䞀方で、改善点もありたした。今回は参加者ずしお興味あるセッションを聞いお知芋を広める目的もありたしたので、事前にグルヌプ内でシフトを決めおいたした。 束尟さん 「シフト衚を䜜成しおいたしたが、自分の番になりブヌスに向かったらみんなブヌスにいたので『あれ』ず思いたした」 叀代さん 「次回ブヌスをやるずきは3人はブヌス内で、他の人はセッション聎講しおむンプットできるようにした方がいいかず。セッションでブログのネタを探すなど、効率化を考える必芁がありたす」 課題ずしお認識したこずはシフトの問題より、時間の効率化になりたす。CIGはブヌス運営者・参加者の二぀の立堎でむベントに参加したため、ブヌス運営ずセッションを聞いお自らの収穫を埗る間のバランス調敎が難しかったず思いたす。 それでも、ちゃんず振り返りをしながら個々で考えおいた課題を明確にするこずができたした。質問ボヌドでKINTOテクノロゞヌズを知っおいたずいう答えが倚かったこずは、長く広報掻動を実斜した成果であり、むベントの運営は単発で終わるこずではない蚌でもありたす。クラりドむンフラGもKINTOテクノロゞヌズの䞀員ずしお技術発信を継続しおいきたすので、今回の参加は今埌の掻動の゚ッセンスになりたした。 CloudNativeの本質 最埌に、みなさんに蚘茉いただいた質問ボヌドの結果を深掘りしたした 癜井さん 「『良いむンフラずは』ずいう質問に察しお、『愛せるこず』ず回答した人が倚かったです。深掘りしおみるず、自瀟サヌビスを愛せるたで理解するこずが、CloudNativeずいう蚀葉の本質の理解を深めるこずの1぀なのかもしれたせん」 叀代さんからは、登壇でも発衚したメッセヌゞを改めお匷調したした。 叀代さん 「CloudNativeを最初から完璧にできおいる組織はありたせん。色々なグラデヌションがありたす。技術だけでなく、文化や組織も含めお䜜っおいく必芁がありたす。スポンサヌブヌスで色々な䌁業ず話しながら、改めお実感したした」 参加者むンタビュヌ Platform G、島村さん 島村さんは、同じグルヌプの李さんの登壇を芋るため、そしお自身もCFPを出しおいたため、どちらにしろ参加する予定でした。そしお、最近チヌムでKubernetesやEKSを觊り始めたこずもあり、技術的な関心も高たっおいたす。 印象に残ったセッション 任倩堂の事䟋 島村さんが最も印象に残ったのは、任倩堂のセッションでした。 島村さん 「オンプレ環境の事䟋ですが、この文化を取り蟌みたいず思いたした。真面目に毎日1回フルでリグレッションテストを回しおいお、バグ報告以倖の目的でもテスト結果を䜿っおいるこずに驚きたした。」 特に興味深かったのは、テスト結果の倚目的掻甚です。 島村さん 「UI的な芳点で、デザむナヌが衚面䞊のデザむン単䜓ではなく『ビルド結果の画像から考えるず、こうした方が(呚りなどの雰囲気ず)マッチする』ずいう刀断材料ずなるそうです。過去からのむベントの流れがあり、その流れで自然かどうかも分かる。声優のアテレコにも䜿甚しおいるそうです。自動テストをバグを芋぀けるだけのものではなく、他の甚途にも䜿う。テストの結果を䜿っおプロアクティブに倚方面で改善点を芋぀けお改善しおいく文化です。」 そしお、任倩堂の事䟋ず匊瀟ずの違いに぀いおも考察しおたしお、 島村さん 「KINTOにはこの文化がただ足りないず思いたした。できるようにしお、気づいお、盎しおいくずいう感芚が遠いです。重くなったりェブサむトは改善しおいく偎面でも必芁ですし、KINTOのサヌビスをより良く提䟛する面もあるず感じたした」 技術的な収穫 島村さんは、いく぀かの技術的な収穫を話しおくれたしお、 Alloyのログ二重転送の原因の気づき 最近ブルヌグリヌンデプロむをした時、Alloyのログが二重転送されおいた原因だず思われる事象に䌌おいるず気付いお、調査のむンプットずしたした。 Dockerビルドのセキュリティ ビルドしたコンテナむメヌゞがマルりェアに汚染されおいないか、眲名や怜蚌のフレヌムワヌクがあるこずを知りたした。昚今の情勢もありたしたので、セキュリティチヌムに情報を共有したした。 RCAの優䜍点の再確認 むンシデント管理のSaaSは、むンシデント管理のやり方だけをAI化しおいたすが、匊瀟では RCA で䞀気通貫で䜜っおいたす。やはり党郚統䞀しお䞀気通貫しないずいけないず、RCAの優䜍点・良かった点を芋盎せたした。 逆に、いく぀かのセッションに察しお疑問を感じたこずも話しおくれたした。 CloudNative技術が課題を解決する手段ではなく、CloudNative技術を䜿うための目的になっおいるこずもあった 知識ずしお埗られるような内容ではなく、それぞれのケヌスに基づいた話を聞きたかった 次回ぞの展望 島村さんは、次回ぞの改善点ず展望を語りたしお、 島村さん 「早めにむベントのセッション登録をしお、自分が聞きたいセッションを確実に聞けるようにしたいです。たた、参加しお埗た情報を個人の䞭に留めず、他のメンバヌが芋れるようにする。こういうこずがある、事䟋があるず知っおもらわないず、参加した時間がもったいない。知識は、広がらないずあたり意味がないず考えおいたす。」 登壇に぀いおも意欲を芋せおくれたした。 島村さん 「CFPを2回萜ちおいるので、次は是非登壇したいです。ただ、ネタが問題です。RCAに぀いおは既に喋り、文化論も Platform Engineering Meetup で喋っおいたす。新しいネタを芋぀ける必芁がありたす」 たずめ 我々はCloudNative Days 2025 Winterぞの参加を通じお、倚くの孊びず気づきを埗るこずができたした。 技術ず文化の䞡茪 登壇者の叀代さんず李さんは、それぞれ異なるアプロヌチでCloudNativeを語りたした。叀代さんは組織文化の芳点から、李さんは技術的な実践の芳点からです。しかし、䞡者に共通しおいたのは、 CloudNativeは手段であり、目的ではない ずいうメッセヌゞです。 ブヌス運営を通じお、私たちは「CloudNativeを最初から完璧にできおいる組織はない」こずを再確認したした。技術だけでなく、文化や組織も含めお䜜っおいく必芁がありたす。 参加者の島村さんが任倩堂の事䟋から孊んだように、CloudNativeは技術ではなく文化です。プロアクティブに改善点を芋぀けお改善しおいく文化、テスト結果を倚目的に掻甚する発想、そしお䜕より、自瀟サヌビスを「愛せる」たで理解するこず。これらがCloudNativeの本質なのかもしれたせん。 チヌムの䞀䜓感 今回のむベントで最も倧きな成果の䞀぀は、むンフラチヌムず改善チヌム、ほが党員がグルヌプの運営偎ずしおむベントに参加できたこずです。みんなで同じむベントの経隓を共有し、孊びを共有できたこずは、チヌムの䞀䜓感を倧きく高めたした。他のグルヌプの方も助けおくださり、ずおも良い時間を過ごす事ができたした。 継続的なアりトプットの重芁性 登壇者の䞡名が共通しお感じたのは、日々のアりトプットの重芁性でした。40分の発衚を準備する倧倉さを経隓し、普段からテックブログや勉匷䌚での発衚を通じお、継続的にアりトプットする習慣の必芁性を実感したした。 たた、島村さんが指摘したように、むベントで埗た情報を個人の䞭に留めず、チヌム・組織で共有するこずも重芁です。知芋を広げるこずで、組織党䜓の成長に぀ながるず信じおたす。 次回ぞの展望 2026幎には、CloudNative䌚議Platform Engineering Meetup、SRE䌚議、CloudNativeDaysの合同むベントが開催されたす。たた、JAWS Daysぞの出展も予定しおいたす。 CloudNativeに悩む皆さんぞ、叀代さんからのメッセヌゞをお䌝えしたす。 「技術に振り回されないでください。ビゞネスの成功、ビゞネスの先にあるミッションの達成のために、䞀緒に悩みながら文化づくり、組織づくりを頑匵りたしょう。CloudNativeは手段であり、目的ではありたせん」 そしお、李さんからのアドバむスも忘れないでください。 「たずは応募しおみるこずが重芁です。恐れずに、たずCFPを出しおみる。採択されおから、未来の自分が䜕ずかしおくれたす」 今回のむベント参加を通じお埗た孊びず気づきを、今埌の組織づくり、文化づくり、そしお技術的な挑戊に掻かしおいきたいず思いたす。コミュニティの皆さんずの継続的な亀流を通じお、共に成長しおいけるこずを楜しみにしおいたす。 [^1]: Cloud Infrastructure Gでは蚘事を䜜成する時点で、むンフラチヌムずカむれンチヌム、゜リュヌションチヌムの䞉぀のチヌムが存圚したす。
Note: This article was translated from Japanese with the assistance of AI. This article is the Day 10 entry of the KINTO Technologies Advent Calendar 2025 🎅🎄 Table of Contents Introduction Summary What We Built Features Prerequisites Operational Best Practices Architecture Implementation Details Usage Results Conclusion Introduction Hi, I'm Miyashita, an engineer in the Common Services Development Group at KINTO Technologies. Modern development environments using AWS and similar platforms are easy to scale up and down, but this flexibility often leads to environment sprawl. Our development setup has grown to include dev, dev2, stg, stg2...stg5, ins, prod, and more. As a result, the values managed in AWS Systems Manager Parameter Store have multiplied (number of environments × number of parameters), leading to frequent mistakes. Here are some examples of errors we encountered: Updated Parameter Store for dev but forgot to update stg KEY and VALUE were correct, but forgot to add tags Deployment failed because we forgot to register a newly required parameter When manually copying from dev to stg, accidentally copied values that should have been environment-specific Additionally, it was difficult to know the current state of Parameter Store values. Checking each environment through the browser was tedious and often got postponed, creating a vicious cycle of more careless mistakes. To solve this, we built an automation system based on the principle of "consolidating all environment parameters in a local YAML file and syncing that file with AWS." This article introduces that approach. :::message For simplicity, this article uses 3 representative environments: dev / stg / prod . In practice, we have many more environments including dev2, stg2-stg5, inte, ins, prod, etc. ::: Summary Automated AWS Parameter Store management for 10+ environments × 50+ parameters because manual management was too cumbersome Visualize all parameters in YAML + one-click sync with GitHub Actions Eliminated update oversights, careless mistakes, and tedious manual copy-paste work What We Built We built this system combining these three components: YAML file - Centralized management of Parameter Store values for all environments Python scripts - Sync processing between YAML and AWS GitHub Actions - One-click deployment to all environments Features 1. YAML Visualization Previously, checking Parameter Store values for each environment required logging into the AWS console multiple times, switching accounts for each environment. Now, opening a single YAML file in the repository shows all parameters for all environments at a glance. parameters: # Parameters with different values per environment - key: api/endpoint description: "API endpoint" environment_values: dev: "https://dev-api.example.com" stg: "https://stg-api.example.com" prod: "https://prod-api.example.com" # Common value across all environments - key: app/timeout description: "Timeout setting (seconds)" default_value: "30" # Sensitive information (SecureString) # Value retrieved from GitHub Secrets via environment variables (e.g., SSM__DB__PASSWORD) - key: db/password description: "Database password" type: "SecureString" Benefits: See at a glance which value is used in which environment Git-managed, so change history is trackable PR reviews can catch value errors before deployment 2. One-Click Sync Simply manually trigger the GitHub Actions workflow, and all environments' Parameter Stores automatically sync with the YAML contents. Using matrix strategy , multiple environments (dev, stg, prod) run in parallel, so execution time stays nearly constant even as environments increase. No more clicking around the AWS console switching between environments. Prerequisites This system works under the following conditions: AWS CLI available on GitHub Actions runners Permissions for Parameter Store: ssm:GetParametersByPath , ssm:PutParameter , ssm:AddTagsToResource GitHub Actions can access AWS (via Access Key or OIDC ) Python 3.11 + pyyaml available :::message This article uses IAM user access keys for simplicity. For production use, we recommend OIDC (OpenID Connect) for keyless authentication to improve security. ::: Operational Best Practices While automation brings convenience, we must also consider the risk of mistakes. Our team implements these practices: Restrict SecureString Operation Permissions We limit who can edit GitHub Secrets, minimizing the number of people who can handle sensitive information. Separate Workflow with Approval for Production When updating Parameter Store for production, we use a dedicated production workflow that includes a Slack approval step. The approval request displays the list of parameters to be updated, so reviewers can confirm "what will change" before approving. This prevents accidental production updates. Architecture graph TB YAML["aws-params.yml"] Secrets["GitHub Secrets (per env)"] subgraph GHA["GitHub Actions"] Workflow["3 environments in parallel"] end subgraph Scripts["Python"] Update["update_aws_params.py"] end subgraph AWS["AWS Parameter Store"] dev["/dev/app/config/"] stg["/stg/app/config/"] prod["/prod/app/config/"] end YAML --> Workflow Secrets --> Workflow Workflow --> Update Update --> dev Update --> stg Update --> prod style YAML fill:#e1f5ff style Secrets fill:#fff0f0 style GHA fill:#fff4e1 style Scripts fill:#f0ffe1 style AWS fill:#ffe1e1 Implementation Details Directory Structure $ tree .github .github ├── aws-params.yml ├── scripts │ ├── aws_param_common.py │ └── update_aws_params.py └── workflows └── sync-parameters.yml YAML Design All environment parameters are consolidated in .github/aws-params.yml (see the "Features" section for YAML examples). Handling SecureString Hardcoding sensitive information like DB passwords in YAML is a security risk. Instead, we separated responsibilities: "YAML contains only key definitions" while "actual values are in GitHub Secrets" . The Python script checks the YAML definition, and if type: SecureString , it reads from the corresponding environment variable. Naming Convention: YAML key: db/password → Environment variable: SSM__DB__PASSWORD Separating SecureString Values by Environment DB passwords often differ by environment. Using GitHub Actions Environments feature, you can set different Secrets for each environment. Setup Steps: Create environments in GitHub repository Settings → Environments ( dev , stg , prod ) Register SSM__DB__PASSWORD etc. in each environment's Secrets (different values per environment) Specify environment: ${{ matrix.env }} in the workflow This way, the dev environment automatically uses dev's DB password, and stg uses stg's. Note: Parameter Store vs Secrets Manager You might think "Shouldn't sensitive information go in Secrets Manager ?" Here's a comparison: Parameter Store (SecureString) Secrets Manager Cost Standard parameters are free $0.40/secret/month Rotation Manual Automatic rotation available Best for API keys with low update frequency When automatic DB password rotation is needed In most cases, Parameter Store (SecureString) is sufficient. Consider Secrets Manager when you need "automatic RDS password rotation." :::message Note: You can manage Secrets Manager values in a similar way. cmd = [ "aws", "secretsmanager", "put-secret-value", "--secret-id", secret_id, "--secret-string", value ] subprocess.run(cmd, check=True) ::: Python Script Structure aws_param_common.py - Common Functions #!/usr/bin/env python3 """AWS Parameter Store Common Functions""" import os import sys import json import subprocess from typing import Dict, Any, Tuple import yaml def get_env_name() -> str: """Get environment name""" env = os.environ.get("ENV_NAME") if not env: print("Error: ENV_NAME environment variable is not set") sys.exit(1) return env def get_prefix(env: str) -> str: """Return prefix based on environment""" return f"/{env}/app/config/" def load_yaml_config() -> Tuple[Dict[str, Any], set]: """Load YAML file""" yaml_path = os.path.join(os.path.dirname(__file__), "..", "aws-params.yml") with open(yaml_path, "r", encoding="utf-8") as f: config = yaml.safe_load(f) yaml_keys = {param["key"] for param in config.get("parameters", [])} return config, yaml_keys def get_existing_params(env: str) -> Dict[str, Dict[str, Any]]: """Get existing parameters from AWS SSM (with pagination support)""" prefix = get_prefix(env) existing_params = {} next_token = None while True: cmd = [ "aws", "ssm", "get-parameters-by-path", "--path", prefix, "--recursive", "--with-decryption", "--output", "json" ] if next_token: cmd.extend(["--next-token", next_token]) try: result = subprocess.run(cmd, check=True, capture_output=True, text=True) data = json.loads(result.stdout) params_data = data.get("Parameters", []) except subprocess.CalledProcessError as e: print(f"Warning: Failed to get parameters: {e.stderr}") return {} for param in params_data: key = param["Name"].replace(prefix, "") existing_params[key] = { "value": param["Value"], "type": param["Type"], "version": param.get("Version", 1) } next_token = data.get("NextToken") if not next_token: break return existing_params def get_param_value(param: Dict[str, Any], env: str) -> str | None: """Get parameter value (SecureString from env vars, others from YAML)""" # For SecureString, get from environment variable if param.get("type") == "SecureString": env_var_name = "SSM__" + param["key"].upper().replace("/", "__") value = os.environ.get(env_var_name) if not value: print(f"Warning: Environment variable {env_var_name} for SecureString {param['key']} is not set") return None return value # Environment-specific value env_values = param.get("environment_values", {}) if env in env_values: return str(env_values[env]) # Default value if "default_value" in param: return str(param["default_value"]) return None def validate_param(param: Dict[str, Any], env: str) -> Tuple[bool, str, Dict[str, Any] | None]: """Validate parameter""" key = param.get("key") if not key: return False, "key is not defined", None value = get_param_value(param, env) if value is None: return False, f"{key}: value for environment {env} is not defined", None param_info = { "key": key, "value": value, "type": param.get("type", "String"), "description": param.get("description", "") } return True, "", param_info def update_parameter(param_info: Dict[str, Any], env: str) -> bool: """Update parameter""" prefix = get_prefix(env) full_name = prefix + param_info["key"] cmd = [ "aws", "ssm", "put-parameter", "--name", full_name, "--value", param_info["value"], "--type", param_info["type"], "--overwrite" ] if param_info.get("description"): cmd.extend(["--description", param_info["description"]]) try: subprocess.run(cmd, check=True, capture_output=True, text=True) add_tags(full_name, env) # Add tags return True except subprocess.CalledProcessError as e: print(f"Error: Failed to update {param_info['key']}: {e.stderr}") return False def add_tags(parameter_name: str, env: str) -> bool: """Add tags to parameter""" cmd = [ "aws", "ssm", "add-tags-to-resource", "--resource-type", "Parameter", "--resource-id", parameter_name, "--tags", f"Key=Environment,Value={env}", "Key=SID,Value=backend-api" ] try: subprocess.run(cmd, check=True, capture_output=True, text=True) return True except subprocess.CalledProcessError as e: print(f"Warning: Failed to add tags: {e.stderr}") return False Key Points: get_existing_params : Pagination support for 50+ parameters get_param_value : SecureString from environment variables, regular parameters from YAML update_parameter : Calls add_tags after parameter update to apply tags About Tags Tags are automatically applied when creating parameters. Tags are useful for searching in the AWS console and cost management, and some systems require tags to read parameters. Tag Value Description Environment dev , stg , prod Environment name automatically set at runtime SID backend-api Service identifier (replace with your service name) update_aws_params.py - Update Script #!/usr/bin/env python3 """AWS Parameter Store Update Script""" import sys import aws_param_common as common def update_parameters(): """Update parameters and report results""" env = common.get_env_name() print(f"=== Environment: {env} ===") print(f"Prefix: {common.get_prefix(env)}") print() config, yaml_keys = common.load_yaml_config() existing_params = common.get_existing_params(env) print(f"Existing parameters: {len(existing_params)}") print() updated_params = [] skipped_params = [] failed_params = [] for param in config.get("parameters", []): is_valid, error_msg, param_info = common.validate_param(param, env) if not is_valid: print(f"[Skip] {error_msg}") continue param_key = param_info["key"] value = param_info["value"] # Compare with existing value if param_key in existing_params: if existing_params[param_key]["value"] == value: print(f"[Skip] {param_key}: no change") skipped_params.append(param_key) continue print(f"[Update] {param_key}: updating value") else: print(f"[New] {param_key}: adding new parameter") # Update parameter success = common.update_parameter(param_info, env) if success: updated_params.append(param_key) print(f" ✓ Done") else: failed_params.append(param_key) print(f" ✗ Failed") # Result summary print() print("=== Summary ===") print(f"Updated: {len(updated_params)}") print(f"Skipped (no change): {len(skipped_params)}") print(f"Failed: {len(failed_params)}") if failed_params: print() print("Failed parameters:") for key in failed_params: print(f" - {key}") sys.exit(1) print() print("✓ Completed successfully") if __name__ == "__main__": update_parameters() Key Points: Skip parameters with unchanged values (prevent unnecessary updates) Output statistics as summary Exit with code 1 on failure GitHub Actions Workflow name: Sync AWS Parameter Store on: workflow_dispatch: # Manual trigger jobs: sync-parameters: runs-on: ubuntu-latest strategy: matrix: env: [dev, stg, prod] environment: ${{ matrix.env }} # Use environment-specific Secrets steps: - name: Checkout repository uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: python-version: '3.11' - name: Install dependencies run: pip install pyyaml - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1 - name: Sync Parameters env: ENV_NAME: ${{ matrix.env }} # For SecureString (from environment-specific GitHub Secrets) SSM__DB__PASSWORD: ${{ secrets.SSM__DB__PASSWORD }} SSM__API__SECRET_KEY: ${{ secrets.SSM__API__SECRET_KEY }} run: | cd .github/scripts python update_aws_params.py Key Points: strategy.matrix runs multiple environments in parallel environment: ${{ matrix.env }} uses environment-specific Secrets (different DB passwords for dev and prod) SecureString values passed to script via environment variables Parameters with unchanged values are automatically skipped Usage 1. Adding/Changing Parameters Just edit .github/aws-params.yml and submit a PR. parameters: # Add new parameter - key: feature/enable_payment_v2 description: "Enable new payment system" environment_values: dev: "true" stg: "false" prod: "false" 2. Deploying to All Environments Open GitHub Actions page Select Sync AWS Parameter Store Click Run workflow button Deploys to all environments in parallel 3. Adding SecureString Parameters Add definition to YAML: - key: payment/api_key description: "Payment API key" type: "SecureString" Register value in GitHub Environments: Settings → Environments → Register in each environment's Secrets (dev, stg, prod) Secret name: SSM__PAYMENT__API_KEY Value: (actual value, different per environment) Add to workflow file's environment variables section: env: SSM__PAYMENT__API_KEY: ${{ secrets.SSM__PAYMENT__API_KEY }} 4. Demo Select and run the action we created from the GitHub Actions page. Confirm the action completed successfully. You can see each environment ran in parallel. Open the AWS Parameter Store console to verify. The parameters are registered. Success! Results Concrete Results Work time : Environments × 5 min → One click (50 min saved for 10 environments) Update oversights : Several per month → Zero Verification : Open AWS console → Just look at YAML Conclusion The "too many environments" problem in the cloud era is a challenge many teams face. The key points of this approach: YAML visualization - Manage all environment parameters in one file One-click sync - Auto-deploy with GitHub Actions SecureString support - Securely manage sensitive information No special technology required—just GitHub Actions + Python + AWS CLI . I hope this helps those struggling with Parameter Store management or dealing with environment sprawl. Thank you for reading!
This article is Day 10 of the KINTO Technologies Advent Calendar 2025 🎅🎄 Introduction I'm Liu (YOU), an Infrastructure Architect in the Cloud Infrastructure Group (CIG) at KINTO Technologies. Starting in November, I've also taken on responsibilities in the Developer Relations Group. In November 2025, our company participated in " CloudNative Days 2025 Winter " as a sponsor. At this event, two of our colleagues gave presentations, the Cloud Infrastructure Group and the Developer Relations Group that I belong to operated the sponsor booth, and many members attended technical sessions, allowing the entire organization to be actively involved. CloudNative Days is the largest event in Japan's cloud-native community. Through this participation, we not only deepened our technical knowledge but also had the opportunity to reconsider the very concept of cloud-native itself. In this article, I'll share the event experience and learnings from the perspectives of speakers, booth operators, and general attendees. Speaker Interviews Koshiro from Cloud Infrastructure Group: " Building the Cloud Foundation for the Future of Mobility Platforms " How I Came to Speak Koshiro : "Having been with the company for less than a year, I didn't know the organization's history or the reasons behind our technology choices. Conveying things I hadn't experienced firsthand was the biggest challenge." Originally, another member was scheduled to speak, but circumstances led to him stepping in at short notice. Having joined in January 2025, this was a challenge for him without fully understanding the organization's history or the background of our technology decisions. Background on Topic Selection He struggled with cloud‑native as a topic. Looking into available resources, he found that many talks went deep into Kubernetes and container technologies. Since our company mainly uses ECS and only uses Kubernetes in limited areas, he couldn’t fully relate. Eventually, he realized that this struggle itself was worth sharing with others who are in the same situation. Koshiro : "Cloud‑native may feel like a high‑level concept and getting there requires organizational trial and error. Technical excellence is important but building a cloud‑native culture within the organization is more important. There is no single best practice." What I most wanted to convey was that cloud-native is a means, not an end . We select appropriate technologies while facing our business needs. By sharing our history of trial and error as it really happened, I hoped to give others facing similar challenges a chance to move forward. Audience Reactions on the Day Venue C, with a capacity of 66 people, was nearly full. Many attendees who had been watching other sessions online moved to this session midway through. Koshiro : "There were people nodding along as they listened, and some took photos of my slides to post on X. At an event where technology seems heavily emphasized, it was striking to see people show interest in my topic." Two people asked questions at the microphone, and three visited "Ask the Speaker," giving me a strong sense of accomplishment for my first presentation. What stayed with me most were the questions about Terraform structure and the platform organization's decision-making. These are questions without definitive answers and I was reminded that everyone struggles with these things . Lessons Learned This was a substantial 40-minute presentation, and through the intensive preparation and practice, Koshiro-san realized how important regular output truly is. Koshiro : "I realized that if I had been giving a presentation regularly, creating slides would have been much easier. If I'd been writing tech blogs, I could have adapted and utilized that content. I've resolved to work harder on my output." He also gained an important insight about using AI. From his initial failed attempt to create a talk script with AI, he learned this lesson: "First establish what message you want to convey, then ask AI how to express it. Never start by asking AI. It's a recipe for failure." Looking Ahead For next time, he wants to build experience with shorter 20-minute presentations and also challenge himself to present more technology-focused topics. Additionally, since it's a community event, he expressed a desire to deepen connections with participants through group work and mutual learning. Koshiro : "Next year, we'll be hosting a cloud-native conference. KINTO Technologies is one of the most advanced organizations within the Toyota Group when it comes to cloud adoption. While we don't have everything figured out yet, we've reached a stage where we can build high-level solutions together. I'd be happy to see more opportunities for collaboration and study sessions." Lee-san from Platform G: " A New Era of Alert Response Pioneered by AI Agents " The Decision to Submit Proposal to CFP Lee-san submitted a proposal to CFP with a "let's just try" attitude, thinking the chances of acceptance were low. The reasons for applying were clear: to enhance the company's external communication power, contribute to engineer recruitment within the group, and gain opportunities for external communication. Since the submission was completely separate from sponsor work, being accepted came as a surprise. Struggles with Material Preparation He struggled most with determining the right skill level for the audience when creating the materials. Lee : "With both experts and beginners in the audience, I struggled to decide what level to aim for. I wanted the content to be easy to understand even for those unfamiliar with the topic, but if I made it too light, it wouldn’t satisfy people looking for deeper knowledge. Striking that balance was the hardest part." I particularly focused on storytelling. I was conscious of the flow: "there was this challenge → we responded this way → the result was this → now we're at this point," and reviewed the overall structure multiple times to make the narrative flow naturally. Material preparation took over a week, continuously improving the script and presentation. Unexpected Events On the day, his biggest concern was whether PowerPoint's script feature would be visible on the audience's screen. For a 40-minute presentation, going without notes would be tough, so during lunch he went to the venue myself to ask for a quick run‑through. Lee : "They provided basic information for speakers, such as arrival times and HDMI connection details, but there should have been more guidance on things like rehearsals and checking script display. For our members who present at future events, I’d recommend visiting the venue in advance to confirm these details themselves." Audience Response The presentation was well-received overall, with fellow speaker Koshiro-san commenting that it was "the most impressive session of the day." After the presentation, 4-5 people came with questions, and Lee-san said he could feel the high interest in AI Agents. What stood out to him most was a question about context engineering: How do you distinguish between information needed for AI and information needed only by humans but not by AI? He shared the countermeasures currently under consideration for this question as well. Lee : "There's the issue of LLM model input limitations, and if the context gets too large, it exceeds the limits. We plan to address this with a single-agent configuration, verifying a method to summarize and compress history using LangChain's new features. I want to talk about these improvements at next year's conferences or in technical articles." Lessons Learned For him, this was the first time speaking at a large-scale open community event, so there was a lot to gain. Lee : "I was nervous, but I found that there was no need to be as scared as I was. If there are people who want to try, I recommend giving it a shot. I was greatly inspired from other speakers and participants, and my technical motivation increased." In terms of technical takeaways, he found the presentations on incident‑management SaaS and CyberAgent’s Ōyama-san’s talk on Loki and Prometheus particularly insightful. He also gained a few dozen new followers on X, which helped broaden his connections within the community. Looking Ahead There are areas for improvement regarding time management. When he practiced his presentation, he noticed that he finished earlier than expected. However, during the actual presentation, he got nervous and couldn’t get the words out. He ended up running short on time and had to skip a page. Next time, he is thinking of giving a talk on improving context engineering and methods for evaluating agents. Lee : "It’s difficult to evaluate the effectiveness of an agent because creating proper evaluation criteria is challenging. Even when alerts seem to be the same, the underlying causes can differ, which makes it hard to build accurate ground‑truth data. This is widely recognized as a common issue across the industry. Context engineering is a field that has recently been gaining attention, but best practices have yet to be established. I believe this is a topic many people are eager to learn about." His advice is clear. Lee : "What matters most is simply applying. Once you get accepted, your future self will figure it out. It boosts your motivation and inspiration, and it also helps raise your company’s presence, benefiting both sides." Other Speakers Tsuji-san, the Manager of Cloud Infrastructure Group, also gave a talk about the Toyota Group's technical community! Tsuji-san from Cloud Infrastructure Group: " Introducing TURTLE, the Toyota Group Engineer Community! " Booth Operation Interview Many people from our company helped with this booth operation. With cooperation from various groups including Cloud Infrastructure Group, Platform Group, HR Group, and Developer Relations Group, we were able to run an exciting booth operation and complete it successfully. For the booth operation interview, we proceeded in a discussion format, with participation from: Cloud Infrastructure Group (CIG): Koshiro, Shirai, Yasuda, Matsuo Developer Relations Group: Murayama Challenges in the Preparation Stage This was CIG's first time operating a booth, so we didn't know what to do, but our company has the Developer Relations Group that fully supports events like this from planning to reflection, enhancing both internal and external communication power. This time too, Murayama-san from the Developer Relations Group provided support from the booth preparation stage. Her collaboration with Manager Tsuji-san was a key factor in successfully running the booth. Murayama : "I consulted with Manager Tsuji-san in advance and briefly communicated what information we wanted. Things progressed through CIG's regular meetings, and before I knew it, the shift schedule was ready, which was very helpful." Murayama : "Also, at a conference we sponsored over the weekend before the event, we displayed a board featuring our system architecture diagram, which was very well received and helped us have many conversations with attendees. So, even though it was the day before the event, I suggested, "Do you have any architecture diagrams we could display?" and they immediately provided the perfect architecture diagram. We barely made it in time for printing, but we managed to have it at our booth. Since it served as a great conversation starter, I'm really glad we were able to prepare it!" I felt the advice was truly valuable, because understanding what attracts people to a booth is something you can’t really grasp without having run many booths yourself. For Cloud Infrastructure Group, many people participated in booth operation for the first time, so most of it was about preparing ourselves to get into the right mindset. Shirai : "I made a conscious effort to stay cheerful and energetic so as not to give off any negative impression. I wanted visitors to have a good impression of our booth." As Shirai-san said, we put a lot of effort into presenting a positive attitude to the people who visited our booth. Observations and Insights on the Day Cloud Infrastructure Group had two main points they wanted to convey at CloudNative Days 2025 Winter. According to Koshiro-san, who spoke at the sponsor session: Koshiro : "First, we wanted people to know what the Cloud Infrastructure Group[^1] does. Second, it was about publicizing the activities of the Solutions Team within our group. Our company does various activities not only for our own services but for the entire Toyota Group. We wanted visitors to know a little about it." Specifically, we communicated with visitors through the question board prepared at the booth: Q3. Do you know KINTO Technologies? Through this question, we were able to broadly communicate our activities as Cloud Infrastructure Group and the company's direction while engaging in conversation. There were also other important insights: Koshiro : "Not all event participants come to the booth, but those who do come and listen are at least interested in KINTO Technologies. Also, I noticed that a certain number of people aren't involved in cloud-native itself. This gap between the event title and the actual attendees stood out to me." Initially, we had the impression that many cloud-native engineers would participate since it was a cloud-native event, but we found that there were more people who want to pursue cloud-native in the future than engineers already doing cloud-native work. In fact, the venue had people from various backgrounds, including app developers, sales representatives, designers, reporters, and students. Yasuda : "I wanted more people to know about KINTO Technologies, but half of the visitors already knew about our company. It was amazing that many people knew about KINTO Technologies from recent events like the Developer Productivity Conference and Gijutsushoten ." We also found that CloudNativeDays participants include very few infrastructure specialists. Many people said that app developers often also handle infrastructure responsibilities, and it's very challenging to write application code while also creating infrastructure resources and handling security. KINTO Technologies has dedicated infrastructure and SRE/security teams like ours, with clearly divided roles, so I strongly feel once again that we can focus on our respective areas of expertise. There were also opinions that it was good to hear various people's perspectives through the question board. We adjusted our questions depending on visitors' backgrounds, personalities, and points of interest. Q1. What's your cloud style? → Work style & Development style Q2. What is "good infrastructure" to you? → "Good work" or "Good system" Matsuo : "I'm glad we could go beyond the questions on the board and hear about people's perspectives and what they prioritize. What left an impression was that surprisingly many people work with cloud services other than AWS. Since I've only used AWS, it made me want to try multi-cloud." Many people with various roles and a strong interest in technology participated: Shirai : "We had visitors with diverse roles, and it was difficult to have deeper conversations with people in roles outside my area of expertise. Going forward, I want to build my foundation (knowledge and communication skills) so I can talk with people in a wide range of roles." Even for CloudNative booth operations, I feel we need to prepare a system that can handle visitors of all roles, not just infrastructure specialist positions. Results and Improvements As one of the biggest achievements this time, we had this conversation: Koshiro : "Almost everyone in the Infrastructure Team and Kaizen Team participated in the event as our group's operators. It's quite a rare experience. I'm grateful we could share the same event experience. While running the event, we were able to share learnings with each other like 'this is how it works' or 'that's how it should be.'" Shirai : "By holding an event with the whole team, the team's sense of unity increased even more. Also, by hearing about what other companies are practicing, we could gain new insights." Participating individually in events is good, but when participating as a team, you can establish the same mindset with each other. I believe we were able to foster team growth and raise awareness, and by working together, we could once again establish a path for continuous technical outreach. On the other hand, there were also areas for improvement. This time, we also had the purpose of listening to sessions we were interested in as participants to broaden our knowledge, so we created a shift schedule within the group in advance. Matsuo : "We created a shift schedule, but when it was my turn and I headed to the booth, I was surprised that everyone was at the booth." Koshiro : "Next time we run a booth, it would be better to have 3 people at the booth while others attend sessions for input. We need to think about ways to be more efficient, like finding blog topics at sessions." What we recognized as an issue wasn't the shift problem but time efficiency. Since CIG participated as both booth operators and attendees, I think it was difficult to balance between running the booth and gaining our own takeaways by attending sessions. Still, by reflecting properly, we managed to bring clarity to the challenges each of us had in mind. Many visitors indicated on the question board that they already knew KINTO Technologies which is a clear result of our ongoing promotional efforts and proof that events aren't just one-time things. The Cloud Infrastructure Group will keep contributing to KINTO Technologies' technical outreach, and this experience will be a key ingredient for our future endeavors. The Essence of Cloud-native Finally, we explored in detail the results of the question board filled out by everyone. Shirai : "For the question 'What is good infrastructure?', many people answered, 'Something you can be passionate about.' Exploring it further, understanding your own service to the point where you can love it might be one way to deepen the understanding of the essence of cloud native." Koshiro-san re-emphasized the message he delivered in his talk: Koshiro : "No organization has cloud-native perfectly figured out from the start. There are various gradations. It's necessary to build not just the technology, but also the culture and organization. Talking with various companies at the sponsor booths, I felt this anew." Attendee Interview Shimamura from Platform Group Shimamura-san planned to attend to see Lee's presentation from the same group, and also he had submitted a CFP himself. Additionally, his team had recently started working with Kubernetes and EKS, so the team's technical interest was growing. Sessions that stood out: Nintendo's Case Study Shimamura-san was amzaed by Nintendo's session. Shimamura : "It was an on-premise case study and I wanted to incorporate this culture. They run full regression tests once a day, and I was surprised they use test results for purposes other than bug reporting." What was particularly interesting was the multi-purpose use of test results. Shimamura : "From a UI perspective, designers don't just look at surface-level design alone but use it as decision-making material like 'considering the build result images, this would (match the surrounding atmosphere) better.' There's a flow of events from the past, and you can tell if it's natural within that flow. They also use it for voice actor dubbing. Using automated testing not just for finding bugs, but for other purposes too. It's a culture of proactively finding and improving points across multiple fronts using test results." And he also reflected on the differences between Nintendo's case and our company: Shimamura : "I felt that KINTO still lacks this culture. This mindset of making things possible, noticing issues, and fixing them doesn't come naturally to us yet. I felt it's necessary not only for improving websites that have become heavy, but also for delivering KINTO's services better." Technical Gains Shimamura-san shared several technical gains: Realizing the cause of Alloy log double-forwarding : When we recently did blue-green deployment, I noticed something similar to what I thought was the cause of Alloy logs being double-forwarded and used it as input for investigation. Docker build security : I learned there are frameworks for signatures and verification to check if built container images are contaminated with malware. Given recent circumstances, I shared this information with the security team. Reconfirming RCA's advantages : While incident management SaaS solutions only apply AI to incident management processes, our company has built an end-to-end solution with RCA. This reaffirmed the advantages and benefits of RCA: that everything must be unified and handled end-to-end. Conversely, he also shared some questions he felt about certain sessions: In some cases, cloud-native technology was becoming the purpose rather than a means to solve problems I wanted to hear case-specific stories rather than general knowledge Looking Ahead Shimamura-san shared improvement points and outlook for next time: Shimamura : "I want to register for event sessions early so I can make sure I attend the ones I’m interested in. Also, instead of keeping the information I gain to myself, I want to make it accessible to other team members. If people don’t know what happened or what examples I found, then the time I spent participating would feel wasted. I believe knowledge doesn’t mean much unless it’s shared and spread." He also showed interest in giving a talk: Shimamura : "My CFP submission was rejected twice, so I definitely want to give a talk next time. However, topics are the problem. I've already talked about RCA, and culture theory at Platform Engineering Meetup . I need to find new topics." Conclusion Through our participation in CloudNative Days 2025 Winter, we gained many learnings and insights. Technology and Culture as Two Wheels Speakers Koshiro-san and Lee-san each talked about cloud-native from different approaches. Koshiro-san from the perspective of organizational culture, Lee-san from the perspective of technical practice. However, what both had in common was the message that cloud-native is a means, not an end . Through booth operation, we reconfirmed that "no organization deploys cloud-native perfectly from the start." We need to build not just technology, but culture and organization as well. As attendee Shimamura learned from Nintendo's case, cloud-native is a culture, not a technology. A culture of proactively finding and improving points, the idea of using test results for multiple purposes, and above all, understanding your own service to the point where you can "love" it. These might be the essence of cloud native. Team Unity One of the biggest achievements of attending this event was that the Infrastructure Team and Kaizen Team—almost everyone—could participate as group operators. Being able to share the same event experience and learnings together greatly increased the team's sense of unity. People from other groups also helped us, and we were able to have a very good time. The Importance of Continuous Output What both speakers commonly felt was the importance of daily output. Having experienced the difficulty of preparing a 40-minute presentation, they realized the need for a habit of continuous output through tech blogs and study session presentations. Also, as Shimamura-san pointed out, it's important not to keep information gained at events to yourself, but to share it with the team and organization. We believe that spreading knowledge leads to organizational growth as a whole. Looking Ahead In 2026, the cloud-native conference will be held (a joint event of Platform Engineering Meetup, SRE Conference, and CloudNativeDays). We also plan to exhibit at JAWS Days. To everyone struggling with cloud-native, here's a message from Koshiro-san: "Don't be driven by technology. Let's work together on culture-building and organization-building, striving together for business success and achieving the mission beyond business. Cloud-native is a means, not an end." And don't forget Lee-san's advice: "It's important to submit an application first. Don't be afraid. Just submit papers. Once you're accepted, your future self will figure it out somehow." We hope to apply the learnings and insights gained through this event participation to future organization-building, culture-building, and technical challenges. We look forward to growing together through continuous interaction with the community. [^1]: At the time of writing this article, Cloud Infrastructure Group consists of three teams: the Infrastructure Team, Kaizen Team, and Solutions Team.
この蚘事は KINTOテクノロゞヌズアドベントカレンダヌ2025 の10日目の蚘事です🎅🎄 目次 はじめに この蚘事の芁玄 䜜ったもの 特城 前提条件 運甚䞊の工倫 アヌキテクチャ 実装のポむント 䜿い方 導入効果 たずめ はじめに こんにちは。KINTOテクノロゞヌズの共通サヌビス開発グルヌプの゚ンゞニア、宮䞋です。 AWSなどを䜿う珟圚の開発環境は、簡単に増やしたり枛らしたりできる反面、環境の数が増えおいきがちです。 我々の開発環境も、dev、dev2、stg、stg2...stg5、ins、prodのように、ずおも倚くなっおしたっおいたす。 そのため、 AWS Systems Manager Parameter Store で管理する倀も、環境数 × パラメヌタ数の組み合わせでどんどん増えおいき、ケアレスミスが倚発しおいたした。 䟋えば以䞋のようなミスです。 devのParameter Storeは最新化したけれど、stgのパラメヌタを曎新し忘れおいた KEY, VALUEは正しいけれど、タグを入れ忘れおいた デプロむに倱敗するので調査したら、新芏远加のパラメヌタを登録し忘れおいたのが原因だった devからstgぞ手䜜業でコピペする際、環境ごずに倉えるべき倀をそのたたコピペしおしたった たた、珟圚のParameter Storeの倀がどうなっおいるかも分かりづらく、ブラりザでいちいち各環境に入っお確認するのが面倒で、぀い埌回しになりがちでした。その結果、よりケアレスミスが起きる悪埪環に陥っおいたした。 そこで、「ロヌカルのYAMLファむルに各環境のパラメヌタを集玄し、そのファむルずAWSを同期する」ずいう方針で自動化の仕組みを䜜りたした。今回はそのアむデアを玹介したす。 :::message 本蚘事では説明を簡朔にするために、代衚ずしお dev / stg / prod の3環境を䟋に説明したす。 実際の珟堎ではこの他に dev2, stg2〜stg5, inte, ins, prodなど、さらに倚くの環境がありたす。 ::: この蚘事の芁玄 10環境以䞊 × 50以䞊のパラメヌタ のAWS Parameter Store管理が煩雑すぎたので自動化した YAMLで党パラメヌタを可芖化 + GitHub Actionsでワンクリック同期 曎新挏れやケアレスミスをれロにし、面倒くさい機械的なコピペ䜜業を無くした 䜜ったもの 以䞋の3぀を組み合わせおこの仕組みを構築したした YAMLファむル - 党環境のParameter Storeの倀を䞀元管理 Pythonスクリプト - YAMLずAWS間の同期凊理 GitHub Actions - ワンクリックで党環境に反映 特城 1. YAML可芖化 埓来は各環境のParameter Storeの倀を確認するために、AWSコン゜ヌルに各環境ごずにアカりントを切り替えお䜕床もログむンする必芁がありたした。 今は、リポゞトリにある1぀のYAMLファむルを開けば、党環境の党パラメヌタが䞀芧できたす。 parameters: # 環境ごずに倀が異なるパラメヌタ - key: api/endpoint description: "API゚ンドポむント" environment_values: dev: "https://dev-api.example.com" stg: "https://stg-api.example.com" prod: "https://prod-api.example.com" # 党環境で共通の倀 - key: app/timeout description: "タむムアりト蚭定秒" default_value: "30" # 機密情報SecureString # 倀はGitHub Secretsから環境倉数経由で取埗䟋: SSM__DB__PASSWORD - key: db/password description: "デヌタベヌスパスワヌド" type: "SecureString" メリット: どの環境でどの倀を䜿っおいるか䞀目瞭然 Git管理できるので倉曎履歎も远える PRレビュヌで倀の間違いを事前に防げる 2. ワンクリック同期 GitHub Actions のワヌクフロヌを手動実行するだけで、党環境のParameter Storeが自動的にYAMLの内容ず同期されたす。 マトリクス・ストラテゞヌ により、耇数環境dev, stg, prodが䞊列で実行されるため、環境が増えおも実行時間はほが倉わりたせん。 AWSコン゜ヌルで環境を切り替えながらポチポチする必芁がなくなりたした。 前提条件 この仕組みは以䞋の前提で動䜜したす。 AWS CLI がGitHub Actionsランナヌ䞊で利甚できるこず Parameter Storeぞの ssm:GetParametersByPath , ssm:PutParameter , ssm:AddTagsToResource 暩限があるこず GitHub ActionsからAWSにアクセスできるこずAccess Keyもしくは OIDC など Python 3.11 + pyyaml が利甚できるこず :::message 本蚘事では簡略化のためにIAMナヌザヌのアクセスキヌを䜿甚しおいたすが、本番運甚ではセキュリティ向䞊のため OIDC (OpenID Connect) を䜿甚したキヌレス認蚌を掚奚したす。 ::: 運甚䞊の工倫 自動化で䟿利になる䞀方、誀操䜜のリスクも考慮が必芁です。我々のチヌムでは以䞋の工倫をしおいたす。 SecureStringの操䜜暩限を絞る GitHub Secretsを線集できる人を限定し、機密情報を扱える人を最小限にしおいたす。 本番環境ぞの反映はワヌクフロヌを分けお承認制に 本番環境のParameter Store曎新時は、本番環境専甚のワヌクフロヌを䜿い、ワヌクフロヌの䞭でSlackで承認ステップを挟む運甚にしおいたす。承認䟝頌時には曎新察象のパラメヌタ䞀芧がSlackに衚瀺されるため、「䜕が倉わるのか」を確認しおから承認できたす。これにより、うっかり本番を曎新しおしたう事故を防いでいたす。 アヌキテクチャ graph TB YAML["aws-params.yml"] Secrets["GitHub Secrets (per env)"] subgraph GHA["GitHub Actions"] Workflow["3環境䞊列実行"] end subgraph Scripts["Python"] Update["update_aws_params.py"] end subgraph AWS["AWS Parameter Store"] dev["/dev/app/config/"] stg["/stg/app/config/"] prod["/prod/app/config/"] end YAML --> Workflow Secrets --> Workflow Workflow --> Update Update --> dev Update --> stg Update --> prod style YAML fill:#e1f5ff style Secrets fill:#fff0f0 style GHA fill:#fff4e1 style Scripts fill:#f0ffe1 style AWS fill:#ffe1e1 実装のポむント ディレクトリ構成 $ tree .github .github ├── aws-params.yml ├── scripts │ ├── aws_param_common.py │ └── update_aws_params.py └── workflows └── sync-parameters.yml YAML蚭蚈 党環境のパラメヌタを .github/aws-params.yml に集玄しおいたすYAMLの䟋は「特城」セクションを参照。 SecureStringの扱い DBのパスワヌドなどの機密情報をYAMLにベタ曞きするのはセキュリティ䞊NGです。 そこで、 「YAMLにはキヌの定矩のみ」「実䜓倀はGitHub Secrets」 ずいう圹割分担を行いたした。 Pythonスクリプト偎で、YAMLの定矩を芋お type: SecureString ならば、察応する環境倉数を読みに行く蚭蚈にしおいたす。 呜名芏則: YAMLのkey: db/password → 環境倉数名: SSM__DB__PASSWORD 環境ごずにSecureStringの倀を分ける DBパスワヌドなどは環境ごずに異なる倀を䜿うこずが倚いです。GitHub Actionsの Environments 機胜を䜿えば、環境ごずに異なるSecretsを蚭定できたす。 蚭定手順: GitHubリポゞトリの Settings → Environments で環境を䜜成 dev , stg , prod  各環境のSecretsに SSM__DB__PASSWORD などを登録倀は環境ごずに異なる ワヌクフロヌで environment: ${{ matrix.env }} を指定 これにより、DEV環境ではDEV甚のDBパスワヌド、STG環境ではSTG甚のDBパスワヌドが自動的に䜿われたす。 補足Parameter Store vs Secrets Manager 「機密情報なら Secrets Manager では」ず思う方もいるかもしれたせん。䜿い分けの目安は以䞋の通りです Parameter Store (SecureString) Secrets Manager 料金 暙準パラメヌタは無料 $0.40/シヌクレット/月 ロヌテヌション 手動 自動ロヌテヌション可胜 向いおいるケヌス APIキヌなど曎新頻床が䜎いもの DBパスワヌドの自動ロヌテヌションが必芁な堎合 倚くのケヌスではParameter StoreSecureStringで十分で、Secrets Managerは「RDSパスワヌドの自動ロヌテヌション」が必芁な堎合に怜蚎しおください。 :::message 補足Secrets Managerの倀も同様に管理できたす。 cmd = [ "aws", "secretsmanager", "put-secret-value", "--secret-id", secret_id, "--secret-string", value ] subprocess.run(cmd, check=True) ::: Pythonスクリプト構成 aws_param_common.py - 共通機胜 #!/usr/bin/env python3 """AWS Parameter Store 共通凊理""" import os import sys import json import subprocess from typing import Dict, Any, Tuple import yaml def get_env_name() -> str: """環境名を取埗""" env = os.environ.get("ENV_NAME") if not env: print("゚ラヌ: ENV_NAME 環境倉数が蚭定されおいたせん") sys.exit(1) return env def get_prefix(env: str) -> str: """環境に応じたプレフィックスを返す""" return f"/{env}/app/config/" def load_yaml_config() -> Tuple[Dict[str, Any], set]: """YAMLファむルを読み蟌む""" yaml_path = os.path.join(os.path.dirname(__file__), "..", "aws-params.yml") with open(yaml_path, "r", encoding="utf-8") as f: config = yaml.safe_load(f) yaml_keys = {param["key"] for param in config.get("parameters", [])} return config, yaml_keys def get_existing_params(env: str) -> Dict[str, Dict[str, Any]]: """AWS SSMから既存のパラメヌタを取埗ペヌゞネヌション察応""" prefix = get_prefix(env) existing_params = {} next_token = None while True: cmd = [ "aws", "ssm", "get-parameters-by-path", "--path", prefix, "--recursive", "--with-decryption", "--output", "json" ] if next_token: cmd.extend(["--next-token", next_token]) try: result = subprocess.run(cmd, check=True, capture_output=True, text=True) data = json.loads(result.stdout) params_data = data.get("Parameters", []) except subprocess.CalledProcessError as e: print(f"譊告: パラメヌタの取埗に倱敗したした: {e.stderr}") return {} for param in params_data: key = param["Name"].replace(prefix, "") existing_params[key] = { "value": param["Value"], "type": param["Type"], "version": param.get("Version", 1) } next_token = data.get("NextToken") if not next_token: break return existing_params def get_param_value(param: Dict[str, Any], env: str) -> str | None: """パラメヌタの倀を取埗SecureStringは環境倉数、それ以倖はYAMLの倀を䜿甚""" # SecureStringの堎合は環境倉数から取埗 if param.get("type") == "SecureString": env_var_name = "SSM__" + param["key"].upper().replace("/", "__") value = os.environ.get(env_var_name) if not value: print(f"譊告: SecureString {param['key']} の環境倉数 {env_var_name} が未蚭定") return None return value # 環境固有の倀 env_values = param.get("environment_values", {}) if env in env_values: return str(env_values[env]) # デフォルト倀 if "default_value" in param: return str(param["default_value"]) return None def validate_param(param: Dict[str, Any], env: str) -> Tuple[bool, str, Dict[str, Any] | None]: """パラメヌタのバリデヌション""" key = param.get("key") if not key: return False, "keyが定矩されおいたせん", None value = get_param_value(param, env) if value is None: return False, f"{key}: 環境 {env} の倀が定矩されおいたせん", None param_info = { "key": key, "value": value, "type": param.get("type", "String"), "description": param.get("description", "") } return True, "", param_info def update_parameter(param_info: Dict[str, Any], env: str) -> bool: """パラメヌタを曎新""" prefix = get_prefix(env) full_name = prefix + param_info["key"] cmd = [ "aws", "ssm", "put-parameter", "--name", full_name, "--value", param_info["value"], "--type", param_info["type"], "--overwrite" ] if param_info.get("description"): cmd.extend(["--description", param_info["description"]]) try: subprocess.run(cmd, check=True, capture_output=True, text=True) add_tags(full_name, env) # タグを远加 return True except subprocess.CalledProcessError as e: print(f"゚ラヌ: {param_info['key']} の曎新に倱敗: {e.stderr}") return False def add_tags(parameter_name: str, env: str) -> bool: """パラメヌタにタグを远加""" cmd = [ "aws", "ssm", "add-tags-to-resource", "--resource-type", "Parameter", "--resource-id", parameter_name, "--tags", f"Key=Environment,Value={env}", "Key=SID,Value=backend-api" ] try: subprocess.run(cmd, check=True, capture_output=True, text=True) return True except subprocess.CalledProcessError as e: print(f"譊告: タグの远加に倱敗: {e.stderr}") return False ポむント: get_existing_params : ペヌゞネヌション察応で50件以䞊のパラメヌタも取埗可胜 get_param_value : SecureStringは環境倉数から、通垞パラメヌタはYAMLから倀を取埗 update_parameter : パラメヌタ曎新埌に add_tags を呌び出しおタグを付䞎 タグに぀いお パラメヌタ䜜成時に、自動でタグを付䞎したす。タグはAWSコン゜ヌルでの怜玢やコスト管理に䟿利なだけでなく、システムによっおはタグがないずパラメヌタを読み蟌めない堎合もありたす。 タグ 倀 説明 Environment dev , stg , prod 実行時の環境名が自動で入る SID backend-api サヌビス識別子自分のサヌビス名に眮き換えお䜿甚 update_aws_params.py - 曎新スクリプト #!/usr/bin/env python3 """AWS Parameter Store 曎新スクリプト""" import sys import aws_param_common as common def update_parameters(): """パラメヌタを曎新し、結果をレポヌトする""" env = common.get_env_name() print(f"=== 環境: {env} ===") print(f"プレフィックス: {common.get_prefix(env)}") print() config, yaml_keys = common.load_yaml_config() existing_params = common.get_existing_params(env) print(f"既存パラメヌタ数: {len(existing_params)}") print() updated_params = [] skipped_params = [] failed_params = [] for param in config.get("parameters", []): is_valid, error_msg, param_info = common.validate_param(param, env) if not is_valid: print(f"[スキップ] {error_msg}") continue param_key = param_info["key"] value = param_info["value"] # 既存の倀ず比范 if param_key in existing_params: if existing_params[param_key]["value"] == value: print(f"[スキップ] {param_key}: 倀に倉曎なし") skipped_params.append(param_key) continue print(f"[曎新] {param_key}: 倀を曎新したす") else: print(f"[新芏] {param_key}: 新芏パラメヌタを远加したす") # パラメヌタを曎新 success = common.update_parameter(param_info, env) if success: updated_params.append(param_key) print(f" ✓ 完了") else: failed_params.append(param_key) print(f" ✗ 倱敗") # 結果サマリヌ print() print("=== 結果サマリヌ ===") print(f"曎新: {len(updated_params)} ä»¶") print(f"スキップ倉曎なし: {len(skipped_params)} ä»¶") print(f"倱敗: {len(failed_params)} ä»¶") if failed_params: print() print("倱敗したパラメヌタ:") for key in failed_params: print(f" - {key}") sys.exit(1) print() print("✓ 正垞終了") if __name__ == "__main__": update_parameters() ポむント: 倀が倉わっおいないパラメヌタはスキップ無駄な曎新を防ぐ 曎新結果を統蚈情報ずしお出力 倱敗時は終了コヌド1で終了 GitHub Actionsワヌクフロヌ name: Sync AWS Parameter Store on: workflow_dispatch: # 手動実行 jobs: sync-parameters: runs-on: ubuntu-latest strategy: matrix: env: [dev, stg, prod] environment: ${{ matrix.env }} # 環境ごずのSecretsを䜿甚 steps: - name: Checkout repository uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: python-version: '3.11' - name: Install dependencies run: pip install pyyaml - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1 - name: Sync Parameters env: ENV_NAME: ${{ matrix.env }} # SecureString甚環境ごずのGitHub Secretsから取埗 SSM__DB__PASSWORD: ${{ secrets.SSM__DB__PASSWORD }} SSM__API__SECRET_KEY: ${{ secrets.SSM__API__SECRET_KEY }} run: | cd .github/scripts python update_aws_params.py ポむント: strategy.matrix で耇数環境を䞊列実行 environment: ${{ matrix.env }} で環境ごずのSecretsを䜿甚devずprodで異なるDBパスワヌドなど SecureStringの倀は環境倉数経由でスクリプトに枡す 倀に倉曎がないパラメヌタは自動的にスキップされる 䜿い方 1. パラメヌタの远加・倉曎 .github/aws-params.yml を線集しおPRを出すだけです。 parameters: # 新しいパラメヌタを远加 - key: feature/enable_payment_v2 description: "新決枈システムの有効化" environment_values: dev: "true" stg: "false" prod: "false" 2. 党環境ぞの反映 GitHub Actionsペヌゞを開く Sync AWS Parameter Store を遞択 Run workflow ボタンをクリック 党環境に䞊列で反映される 3. SecureStringパラメヌタの远加 YAMLに定矩を远加 - key: payment/api_key description: "決枈APIキヌ" type: "SecureString" GitHub Environmentsに倀を登録 Settings → Environments → 各環境dev, stg, prodのSecretsに登録 Secret名: SSM__PAYMENT__API_KEY 倀: 環境ごずに異なる実際の倀 ワヌクフロヌファむルの環境倉数セクションに远加 env: SSM__PAYMENT__API_KEY: ${{ secrets.SSM__PAYMENT__API_KEY }} 4. 実挔 GitHub Actions画面から今回䜜ったアクションを遞んで、起動したす。 アクションが正垞終了したこずを確認したす。各環境が䞊列で動䜜した事がわかりたす。 AWSのパラメヌタストア画面を開いお確認しおみたす。パラメヌタが登録されおいたす。成功です。 導入効果 具䜓的な効果 䜜業時間 : 環境数 × 5分 → ワンクリック 10環境なら50分削枛 曎新挏れ : 月数回発生 → れロ 確認䜜業 : AWSコン゜ヌルを開く → YAMLを芋るだけ たずめ クラりド時代の「環境が増えすぎ問題」は、倚くの珟堎で盎面しおいる課題だず思いたす。 今回玹介したアむデアのポむントは YAML可芖化 - 党環境のパラメヌタを1ファむルで管理 ワンクリック同期 - GitHub Actionsで自動反映 SecureString察応 - 機密情報も安党に管理 特別な技術は䜿っおおらず、 GitHub Actions + Python + AWS CLI だけで実珟できたす。 Parameter Storeの管理で困っおいる方、環境が増えお運甚が倧倉になっおいる方の参考になれば幞いです。 最埌たでお読みいただき、ありがずうございたした🙇‍♂
この蚘事は KINTOテクノロゞヌズ Advent Calendar 2025 の9日目の蚘事です🎅🎄 初めに こんにちは。11月に KINTOテクノロゞヌズ に入瀟した蟻勝利です。 私は20幎以䞊、デゞタルアクセシビリティの分野で働いおきた゚ンゞニアで、生たれたずきから党盲の芖芚障害者です。 この蚘事では、なぜ芖芚障害者の私がモビリティを専門ずするKINTOテクノロゞヌズに転職したのか、そしお今埌どんな未来を目指しおいるのかをお話ししたす。少し未来を芋据えた、倢のような話になるかもしれたせんが、ぜひ最埌たでお付き合いください。 芖芚障害者ず「移動」の課題 私は長厎県䜐䞖保垂で生たれたした。長厎ずいえば坂の町。電車よりもバスが普及しおいたしたが、私の家はバス停からも距離があり、芖芚障害者が独力で日垞生掻を送るのは珟実的ではありたせんでした。 子䟛のころ、各家庭には自家甚車があり、移動は車が䞭心。私が移動するには、家族に車を運転しおもらうしかありたせんでした。 30幎ほど前に䞊京したずき、駅たでの道を芚えれば電車で自由に移動できるこずに感動したのを芚えおいたす。それほど、地方での生掻は私にずっお「移動の自由」がないものでした。 今䜏んでいる堎所も駅から遠く、芖芚障害者には䞍䟿な環境です。しかし、リモヌトワヌクで仕事をしながら、窓から聞こえる鳥の声に耳を傟ける生掻はずおも心地よいものです。芖芚障害者の知人からは「䞍䟿ではないか」ず心配されたすが、3幎䜏んでみお、日垞生掻に倧きな䞍䟿は感じおいたせん。 自動運転ぞの憧れ 皆さんは「車の自動運転」にどれくらい興味がありたすか 私はこの堎所に匕っ越しおから、自分の意思で自由に移動できる可胜性を秘めた自動運転に匷く惹かれるようになりたした。芖芚障害者である私にずっお、自動運転は単なる技術ではなく、「移動の自由」を取り戻す倢です。 きっかけは自動運転タクシヌの動画 初めお芖芚障害者が自動運転車に乗る様子を芋たのは、2012幎にGoogleが公開した動画でした。圓時は「本圓に実珟できるのだろうか」ず半信半疑でした。 https://www.youtube.com/watch?v=cdgQpa1pUUE しかし2024幎、自動運転タクシヌに芖芚障害者が乗車する動画を芋お驚きたした。スマヌトフォンを操䜜し、クラクションの音で車の䜍眮を確認しお乗車する仕組み。しかも、アメリカの公道で実際に走っおいるのです。 この瞬間、「自分も詊しおみたい」ず匷く思いたした。 https://www.youtube.com/watch?v=4OPiC-zXtJk&list=PLzaPe49p32aZ7SPFDhvgJV-cOpskQ5VYu&index=7 「KINTOテクノロゞヌズであれば、自動運転の技術にかかわるこずができるかもしれない。い぀かは自分も自動運転にかかわれるような仕事がしたい。」 これが、私がモビリティカンパニヌで働きたいず考えたきっかけです。長幎培ったアクセシビリティの知識を掻かし、芖芚障害者の移動に貢献したいず考えおいたす。 KINTOテクノロゞヌズで目指すこず 自動運転車が芖芚障害者にずっお圓たり前になる未来を目指すずしおも、すぐに実珟できるわけではありたせん。倚くの人にずっお「芖芚障害者」ず「車」は最も瞁遠い組み合わせでしょう。 前述のように、芖芚障害者が誰かに車を運転しおもらっお目的地に行くこずは想像できたすが、単独で車に乗車しお目的地たで行くこずはあたり想像できないのではないでしょうか しかし、このようなニヌズは確かにあっお、特に地方で暮らす芖芚障害者は自分だけで行きたいずころにい぀でも出かけられるような未来を倢芋おいたす。 公共亀通機関を利甚すれば目的地の近くたで移動するこずはできたすが、そこから目的地を探しお移動するこずは容易ではありたせん。 䟋えば、歩行者甚のGPSナビゲヌションを䜿っお移動したずしおも、目的地が近づくず「たもなく目的地付近です」ずいう案内ずずもにナビゲヌションは終了しおしたいたす。 そこからのほんの数メヌトルが、芋るこずのできない芖芚障害者にずっおは倧きなハヌドルずなるこずがあるのです。 このように、「芖芚障害者」ず「移動」に眮き換えれば、目的を達成するために移動するこずは誰もが容易に理解できるかず思いたす。その手段が「自動運転車」になる未来を䜜るために、私は次のこずに取り組みたす。 1. アクセシビリティを組織の圓たり前にする モビリティ業界では、アクセシビリティの重芁性はただ十分に認識されおいたせん。私は瀟内で䞁寧に察話し、 なぜアクセシビリティが必芁なのか チヌムで䜕から始めるべきか どんなゎヌルを目指すのか を䞀緒に考えおいきたす。 皆さんずずもに掻動し、これたで想定されおいなかったかもしれないナヌザヌのニヌズや、どんなこずに䞍䟿を感じたり、どんなふうに課題を解決しおいるのかを芋おいただくこずで、「アクセシビリティは自分たちには関係ない」ず思われない組織にするこず。それが最初の1幎の目暙です。 その結果、KINTOテクノロゞヌズが取り組む様々な掻動のアクセシビリティが向䞊し、新たなナヌザヌにリヌチできお、ゆくゆくはそれが組織の䟡倀になり、瀟䌚を倉えるような取り組みになればいいなず考えおいたす。 2. 障害者が働き続けたい組織を䜜る 50人以䞊の䌁業には障害者雇甚が矩務付けられおいたすが、実際には「どう接すればいいかわからない」「どんな仕事を任せればいいのか想像できない」ずいう声もありたす。 定型化された仕事だけを任され、䌁業の䞭で孀立しおしたうケヌスも少なくありたせん。 KINTOテクノロゞヌズには私のほかにも障害のある瀟員がいたす。私の圹割は、私たち障害者が組織で働く姿を芋おもらい、同僚ずしお共に䌚瀟の目暙に向かっお進める環境を䜜るこずです。 そしお、私たち自身が「ここで長く働き続けたい」ずいえるような組織を䜜っおいきたいず思っおいたす。 「組織の内偎からしか倉えられない䟡倀」を最倧化し、KINTOテクノロゞヌズの発展に貢献しおいきたす。 最埌に 芖芚障害者にずっお「移動の自由」は人生を倉える力を持っおいたす。私はKINTOテクノロゞヌズで、その未来を珟実に近づけるために挑戊したす。 この取り組みに共感しおいただける方がいれば、ぜひ䞀緒に「移動の未来」を考えおいきたしょう。
Introduction Hello, and thank you for reading! I'm Nakamoto, a frontend developer at KINTO FACTORY . This time, rather than discussing technical topics, I'd like to share my experience taking paternity leave when my son was born in August this year. I'll cover the handover process at the team level and my thoughts after returning to work. Consulting with My Manager In April of this year, I was assigned as the team leader for the frontend team within the FACTORY E-commerce Development Group. At first, I felt a bit hesitant about being away from work for an extended period, but when I consulted with my manager, he wholeheartedly encouraged me to take paternity leave. I was told that the first few months after birth are particularly demanding for mothers, both physically and mentally, so I should try to support my wife as much as possible. So I applied for about two months of paternity leave, with the plan to reassess after one month whether I could return early. Handover Items Now, when it came to handing over my daily responsibilities, in addition to my regular frontend development work, as team leader I handled: Reviewing architecture for new projects, making directional decisions, and coordinating with related departments 1-on-1 meetings with each team member Semi-annual reviews and evaluations with each team member Recruiting activities Let me go into detail on each of these. Reviewing architecture for new projects, making directional decisions, and coordinating with related departments After regularly checking the roadmap with the PdMs and my manager for late August when my leave was scheduled to start, it turned out there weren't any major new projects coming up and most were already in progress. So I started documenting the projects I had been handling in Confluence, making it a habit to briefly summarize things like "who I discussed this with and what was decided" and "what I've completed so far." 1-on-1 meetings with each team member Semi-annual reviews and evaluations with each team member Next was the management area. One of my missions as team leader was to review and evaluate each team member's progress on a semi-annual basis. That review period was going to overlap directly with my leave period, so from the start of the April term, I had each team member set their goals in advance to make the reviews go smoothly. In practice, each member would record their goals and write monthly updates in Confluence about "what they accomplished and what they plan to do next month," which served as discussion topics for our 1-on-1s. I also made notes in that same Confluence page about things I noticed and the efforts I appreciated, so I could provide feedback during reviews. Since I was planning to take leave around the time of the birth, I figured that if my leave overlapped with the evaluation period, I could simply hand over that Confluence page to my manager, allowing him to quickly review each member's goals and achievements. Recruiting activities For this, I basically asked my manager to take over. However, I wanted the development team members to get a sense of candidates' technical skills and communication abilities from the interview stage, so I had them join interviews to check on communication and technical skills at the team level. During My Leave With most of the handover preparations in place, on August 24th, our healthy baby boy was born! I took three days of special leave, going back and forth between local government offices and the hospital. On the day before my paternity leave officially started, I went to the office just once to return my company equipment (PC, phone, employee badge, etc.). From there, for about two months, I stepped back from work and fully committed to taking care of my baby! Childcare Is Tough The FACTORY E-commerce Development Group has plenty of experienced dads, and before the birth I'd heard all kinds of stories, but as expected, the first month was really hard. My wife and I split childcare and sleep into half-day shifts and I handled childcare from morning until night. My wife suggested this approach, saying it would help me maintain my sleep cycle for when I returned to work, so I was basically up during the day and sleeping at night. With feeding, diaper changes, soothing the baby, and bath time coming at me almost by the minute, I barely had any time to think about myself. I don’t think work even crossed my mind at all during the first week or two. Still, the Service Is on My Mind About a month into my leave, I was able to talk with my manager and get updates on project progress and the team's status. After a month of hardly going out and barely talking to anyone, it felt like a huge mental reset. Also, by this point I'd gotten quite used to childcare, and my son was settling into longer sleep periods, so I remember occasionally checking on service updates. KINTO FACTORY releases new products and features on Wednesdays, so every Wednesday I'd visit the site to see "what's new this month?" The Return And so, after 65 days of paternity leave, I returned to work in November. There weren't any major changes to the group members, and some ongoing projects had been pushed back. Overall things hadn't changed dramatically from before my leave, so I was able to return smoothly. However, since my wife suddenly had to handle childcare solo during the day, I try to reduce her burden by going to work as early as possible in the morning and coming home as early as possible in the evening. This is made possible by our full-flex system. As long as I coordinate meetings and get agreement from the team, I can freely adjust my working hours. Conclusion By dedicating myself to childcare during the first two months, I was able to witness my son’s growth up close, from his very first smiles to his little coos and all the small changes he made day by day. Being so involved during this period felt like an incredibly precious experience. I was only able to have this experience because my manager and everyone in the group warmly sent me off on paternity leave. I'd like to take this opportunity to express my gratitude. At KINTO Technologies, I believe it's an environment where men can take paternity leave without difficulty. In fact, I've heard that many male engineers across different divisions have taken paternity leave just this year alone (there are many kids the same age as my son in the company!). I hope this is helpful for anyone preparing to take paternity leave or getting ready to return to work. Also, the article below introduces a day in the life of an experienced dad in the same FACTORY E-commerce Development Group, so please check that out too! A Must-Read for Parent Engineers! A Day in the Life of a KTC Dad Engineer
This article is for Day 9 of the KINTO Technologies Advent Calendar 2025 🎅🎄 Introduction Hello. I'm Katsutoshi Tsuji, and I joined KINTO Technologies in November. I'm an engineer who has worked in the digital accessibility field for over 20 years, and I have been completely blind since birth. In this article, I'll share why I, a visually impaired person, chose to join KINTO Technologies, a company specializing in mobility, and what kind of future I'm working toward. This might sound like a futuristic dream, but please stick with me to the end. The Challenges of Mobility for Visually Impaired People I was born in Sasebo City, Nagasaki Prefecture. Nagasaki is known as a city of hills. Buses were more common than trains, but my home was far from the bus stop, making it unrealistic for a visually impaired person to live independently. When I was a child, every household had a car, and transportation centered around driving. The only way I could get around was to have my family drive me. When I moved to Tokyo about 30 years ago, I remember being moved by the realization that once I memorized the route to the station, I could travel freely by train. That's how much my life in the countryside lacked freedom of movement. The place where I currently live is also far from the station, which is inconvenient for visually impaired people. However, working remotely while listening to the birds singing outside my window is a very pleasant lifestyle. Some of my visually impaired acquaintances worry that it might be inconvenient, but after living here for three years, I haven't experienced any major difficulties in daily life. My Dream of Autonomous Driving How interested are you in autonomous driving? Since I moved here, I have found myself increasingly fascinated by autonomous driving and the possibility it offers: the freedom to move on my own terms. For me, as a visually impaired person, autonomous driving is more than just technology. It is a dream of reclaiming freedom of mobility. A Video of an Autonomous Taxi Sparked My Interest The first time I saw a visually impaired person riding in an autonomous vehicle was in a video released by Google in 2012. At the time, I was skeptical, wondering if it could really become a reality. https://www.youtube.com/watch?v=cdgQpa1pUUE However, in 2024, I was amazed when I saw a video of a visually impaired person riding in an autonomous taxi. The system allows passengers to operate their smartphone and locate the vehicle by the sound of its horn. What's more, it was actually running on public roads in the United States. At that moment, I strongly felt that I wanted to try it myself. https://www.youtube.com/watch?v=4OPiC-zXtJk&list=PLzaPe49p32aZ7SPFDhvgJV-cOpskQ5VYu&index=7 I thought, at KINTO Technologies, I might be able to get involved with autonomous driving technology. Someday, I want to work on projects related to autonomous driving. This is why I decided to work at a mobility company. I want to leverage the accessibility knowledge I've built over the years to contribute to mobility for visually impaired people. What I Aim to Achieve at KINTO Technologies Even if we aim for a future where autonomous vehicles become commonplace for visually impaired people, it won't happen immediately. For most people, visually impaired person and car are probably the most unlikely combination. As I mentioned earlier, you can probably imagine a visually impaired person being driven somewhere by someone else, but it's harder to imagine them getting into a car alone and traveling to their destination, right? However, this need definitely exists, and especially visually impaired people living in rural areas dream of a future where they can go wherever they want, whenever they want, by themselves. While public transportation can get you close to your destination, finding and navigating to the actual location from there is not easy. For example, even if you use pedestrian GPS navigation to travel, as you approach your destination, the navigation ends with a message like "You are approaching your destination." Those few meters from there can be a huge hurdle for visually impaired people who cannot see. In this way, if we replace "car" with "mobility" in the context of visually impaired individuals, everyone can easily understand that the freedom to get around is essential for achieving one's goals. To create a future where autonomous vehicles become a key means of mobility, I will work on the following. 1. Making Accessibility the Norm in the Organization In the mobility industry, the importance of accessibility is not yet fully recognized. I will engage in careful dialogue within the company to explore together: Why accessibility is necessary What teams should start with What goals we should aim for By working alongside everyone and showing them the needs of users who may not have been considered before, what inconveniences they experience, and how they solve problems, my goal for the first year is to build an organization where people don't see accessibility as someone else's problem. As a result, I hope that the accessibility of various initiatives undertaken by KINTO Technologies will improve, reach new users, eventually become a value for the organization, and lead to efforts that change society. 2. Creating an Organization Where People with Disabilities Want to Continue Working Companies with 50 or more employees are required to hire people with disabilities, but in reality, some say, I don't know how to interact with them, or I can't imagine what kind of work to assign them. There are many cases where employees are only given standardized tasks and become isolated within the company. I am not the only employee with a disability at KINTO Technologies. My role is to demonstrate how people with disabilities contribute to the organization, and to foster an environment where we can work together as colleagues toward our company's goals. And I want to create an organization where we can truly say, "I want to keep working here." I will maximize the value that can only be changed from inside the organization and contribute to the growth of KINTO Technologies. Conclusion For visually impaired people, freedom of movement has the power to change lives. At KINTO Technologies, I will take on the challenge of bringing that future closer to reality. If anyone resonates with this initiative, I would love to think about the future of mobility together.
こんにちは 人事䌁画G 劎務・総務チヌムの぀ん぀んです。 構想から玄1幎。䜕もないガランずした空間だった堎所が、぀いに「働きたくなるオフィス」ぞず生たれ倉わりたした。 今回は、写真や動画だけでは䌝えきれない、私たちの「こだわり」ず「熱量」を詰め蟌んだ犏岡の新オフィスツアヌぞご案内したす。壁玙䞀枚、怅子䞀脚にたでストヌリヌがあるんですよ。 1. 幞せが溢れおくる゚ントランス たず皆様をお迎えするのは、ガラス匵りの芖界が良い緑豊かな゚ントランスです。 ただ鉢怍えを眮くだけでは面癜くないですよね。そこで、あえお段差を぀けお怍物を配眮し、空間に「枠」を䜜りたした。 こうするこずで、倖からも䞭からも緑が映え、空間がキュッず締たっお芋えるんです。たさに「䞀石二鳥」のアむデア。 「幞せが溢れおくる入り口」 ――手前味噌ですが、そんな衚珟がぎったりの空間になりたした。 2. 倧人心をくすぐる「枯」のラりンゞずカりンタヌ 犏岡ずいえば「枯」。ラりンゞ゚リアのテヌマはずばり「Port枯」です。 ここで絶察に芋おほしいのが、壁面のデザむン。これ、䜏宅の倖壁などに䜿われる**「ガルバリりム」**ずいう本物の建材を䜿っおいるんです。 実はこれ、ただの食りじゃありたせん。このコンテナロックはガラガラず動かすこずができるんです。 たたカりンタヌ奥には ハンドドリップでコヌヒヌを淹れたり、業務終了埌にはバヌカりンタヌのように䜿っおむベントを行ったりするこずも想定しおいたす。 裏 衚 たた䌑憩゚リアの照明には船舶颚のマリンランプや、ガラスの浮き球を暡した照明を吊るし、船内のような枩かみのある雰囲気を挔出しおいたす。现郚たで「枯」を感じさせる、倧人心をくすぐる空間です。 3. 「WE BUILD BRIDGES」絶景のラりンゞ゚リア 窓際は、このオフィス䞀番の特等垭です。 目の前には倩神の街ず橋が広がり、開攟感は抜矀。この壁には倧きく 「WE BUILD BRIDGES」 のアヌトワヌクを斜したした。犏岡で有名な荒接倧橋からヒントを埗お拠点長が各拠点が぀ながるようにずいう想いをこめお瀟内のクリ゚むティブが䜜成したした。 たた枯方面には怅子を眮かず、「スタンディング゚リア」にしおいたす。 海を眺めながら仕事するずアむデアが浮かんでくるかもですね。 倕方 倜 倜は倜景が綺麗でしおたた違った雰囲気を出しおおりたす〜 ふず芖線を䞊げれば、窓の倖に荒接倧橋が芋えたす。昌間の写真 煮詰たった時にここに来お、倖を眺めながら立っお議論すれば、新しいアむデアの架け橋がかかるかもしれたせん。もちろん、電源も完備しおいるので集䞭䜜業もバッチリですよ。 4. 䌚議宀゚リア 執務・䌚議宀゚リアは、雰囲気をガラッず倉えお倧阪オフィスずはたた違う、萜ち着いた倧人の雰囲気です。 䌚議宀の番号フォント、少し倉わっおいるず思いたせんか 実はこれ、ステンシルフォントのデザむンフォントを採甚しおいるんです。IT䌁業らしい遊び心をこっそり忍ばせおいたす。 䌚議宀぀の内぀はあえお「キャスタヌタむダがない」モデルを遞びたした。 䞍䟿そうですか いえいえ、キャスタヌがないず垭を立った時に怅子が散らばらず、元に戻そうず心理が働き垞に敎然ず元の䜍眮に戻るんです。矎しい空間を保぀ための、私たちの矎孊であり、こだわりポむントです。 たた青色の壁はOsaka Tech Labず同じ青を䜿うこずにより、西日本の䞀䜓感を創出しおたす。 5. 芋えない堎所にこそ「愛」を 最埌に、こっそりお芋せするのが、特泚のベンチ゜ファの䞋です。 座面をパカッず開けるず、そこには灜害甚備蓄品が入っおいたす。 おしゃれなデザむンの裏偎に、瀟員の安党を守る備えもしっかり隠しおいたす。「䜕かあった時でも、ここなら安心」ず思える堎所にしたかったんです。 おわりにこだわり抜いた「柱」の話 実はこのオフィスを䜜る際、柱のタむル装食だけに予算をかけるか非垞に悩みたしたそれでも、「空間の空気感を倉えるためには必芁」ず感じたした。タむルを貌らなくおも堎所ずしおは成立したすが、気持ちよく仕事をしおほしい・・・私たちのそんな想いが詰たっおいたす。たた本棚ず䞀緒にするこずによりスペヌスを有効掻甚したした。 コヌヒヌの銙り、コンテナ扉の重厚感、そしお窓からの玠晎らしい眺め。 ぜひ䞀床、遊びに来おください。このこだわりの堎所で、皆様ずお䌚いできるのを楜しみにしおいたす 今埌もオフィスの改善掻動を続けおいきたすので、Tech Blogでの報告を楜しみにしおいおくださいね。
この蚘事は KINTOテクノロゞヌズ Advent Calendar 2025 の 8 日目の蚘事です🎅🎄 KINTOテクノロゞヌズのAndroid゚ンゞニア 山田 剛 です。 本蚘事では、少しのコヌド远加・倉曎でJetpack Composeを利甚したUIにアニメヌションを远加し、アニメヌションの印象を向䞊させるための事䟋集を玹介したす。 1. はじめに スマヌトフォンアプリの印象を倧きく巊右する芁玠の䞀぀がアニメヌションです。適所に甚意された気の利いたアニメヌションは、ナヌザヌの操䜜に察する芖芚的なフィヌドバックを提䟛し、アプリの動䜜の意味を理解しやすくするずずもに、アプリの印象を向䞊させ、品質に察するナヌザヌの信頌感を増したす。Jetpack Composeでは、「宣蚀的UI」の特性を掻かした、埓来のViewシステムよりも短く簡朔なコヌドで、生産性の高いアニメヌションの実装が可胜になっおいたす。本蚘事では、その技術の䞭から、小芏暡なコヌドの远加・修正で既存の゜ヌスに容易にアニメヌションを远加できる実甚的なテクニックを玹介したす。 本蚘事では、執筆時点での Compose Animation の最新の安定版 1.10.0 を含んだ Jetpack Compose Libraries BOM 2025.12.00 で゜ヌスコヌドを怜蚌しおいたす。 2. 座暙を指定するタむプのUI郚品のアニメヌション 以䞋は簡単なパズルゲヌム15パズルを短いコヌドで曞いおいたす: @Composable fun Puzzle15(modifier: Modifier = Modifier) { var puzzleState by remember { mutableStateOf(PuzzleState.generate()) } var moves by remember { mutableIntStateOf(puzzleState.moves) } val solved = puzzleState.isSolved() val titleStyle = MaterialTheme.typography.headlineLarge.merge(fontWeight = FontWeight.W600) val movesStyle = MaterialTheme.typography.titleMedium val solvedStyle = MaterialTheme.typography.titleLarge.merge(color = Color.Green, fontWeight = FontWeight.W600) val buttonStyle = MaterialTheme.typography.titleMedium.merge(fontWeight = FontWeight.W600) Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) { Column( modifier = modifier .fillMaxSize() .padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Text( text = "15 Puzzle", style = titleStyle, modifier = Modifier.padding(bottom = 16.dp) ) Text(text = "Moves: $moves", style = movesStyle) PuzzleGrid( puzzleState = puzzleState, modifier = Modifier.padding(vertical = 24.dp) ) { index -> if (solved) return@PuzzleGrid puzzleState.moveTile(index) moves = puzzleState.moves } Button(onClick = { puzzleState = PuzzleState.generate() moves = 0 }) { Text("New Game", style = buttonStyle) } } if (solved) { Text(text = "🎉 Solved! 🎉", style = solvedStyle) } } } 4x4 の升目に描く15枚のタむルは、以䞋のコヌドで衚瀺しおいたす: @Composable private fun PuzzleGrid( puzzleState: PuzzleState, modifier: Modifier = Modifier, onTileClick: (Int) -> Unit ) { BoxWithConstraints( modifier = modifier .fillMaxWidth() .aspectRatio(1F) ) { val gridSize = maxWidth val tileSize = (gridSize - 12.dp) / 4 // 3 gaps of 4dp for (position in 0.until(PuzzleState.GRID_COUNT)) { val value = puzzleState.tileAt(position) if (value == 0) continue val targetOffset = DpOffset( x = (tileSize + 4.dp) * (position % 4), y = (tileSize + 4.dp) * (position / 4) ) PuzzleTile( value = value, onClick = { onTileClick(position) }, modifier = Modifier .offset(x = targetOffset.x, y = targetOffset.y) .size(tileSize) ) } } } ナヌザヌは動かせるタむルをタップするこずで空いた升目にタむルを移動させる操䜜を繰り返し、パズルの完成を目指したす。タむルは1手で最倧3枚同時に動かせたす。これだけでもシンプルゲヌムずしお充分楜しめたすが、リアル䞖界のパズルゲヌムのように物理的にタむルを動かしおいる感觊がほしいずころです。それをアニメヌションで衚珟するこずを目指したす。 なお、論理的なパズルの状態を衚すクラス PuzzleState は以䞋のようになっおいたす: class PuzzleState private constructor(private val tiles: IntArray) { var moves = 0 private set fun isSolved(): Boolean = tiles.all { tiles[it] == it + 1 || it == 15 } private fun getMoveOffsetOrZero(index: Int): Int { val emptyIndex = tiles.indexOf(0) val row = index / 4 val col = index % 4 val emptyRow = emptyIndex / 4 val emptyCol = emptyIndex % 4 return when { row == emptyRow -> { if (col < emptyCol) 1 else -1 } col == emptyCol -> { if (row < emptyRow) 4 else -4 } else -> 0 } } fun moveTile(index: Int) { val offset = getMoveOffsetOrZero(index) if (offset == 0) return var position = index do { position += offset } while (position >= 0 && position < tiles.size && tiles[position] != 0) do { val next = position - offset tiles[position] = tiles[next] position = next } while (position != index) tiles[index] = 0 moves += 1 } fun tileAt(index: Int) = tiles[index] companion object { const val GRID_COUNT = 16 fun generate(): PuzzleState { val tiles = IntArray(GRID_COUNT) { it } // シャッフル解ける配眮のみ生成 do { tiles.shuffle(Random.Default) } while (!isSolvable(tiles)) return PuzzleState(tiles) } private fun isSolvable(tiles: IntArray): Boolean { val inversions = (0..15).sumOf { idx -> (idx + 1 until tiles.size).count { tiles[idx] != 0 && tiles[it] != 0 && tiles[idx] > tiles[it] } } // 空癜が奇数行䞋からにあり、転眮数が偶数の堎合、たたは空癜が偶数行にあり、転眮数が奇数の堎合は解ける return (3 - tiles.indexOf(0) / 4) % 2 == inversions % 2 } } } 2.1. 座暙オフセットの状態を保持する State を定矩する 以䞋のコヌドでは、修正前の PuzzleGrid(...) の targetOffset を animateValueAsState(...) で倉換しお新たに animatedOffset を定矩し、 PuzzleTile(...) の Modifier.offset(...) の匕数を眮き換えおいたす。 animatedOffset は targetOffset ず同じオフセットを衚珟し぀぀、タむルを移動させるずきの移動前から移動埌のオフセットの倉化をなめらかに衚珟する機胜を持っおいたす。 ただし、この䟋の堎合はこの12行のコヌド远加だけではアニメヌションせず、 // 各タむル倀の珟圚䜍眮を远跡 ずコメントされた郚分の衚瀺順の゜ヌトを行った倉数 tilePositions を䜿う必芁がありたした。これは、 PuzzleGrid(...) における for ルヌプ内でのタむルの順番を垞にタむルに描かれた数字の順に保぀ための䞊べ替えを行っおいたす。このようにするこずで、Jetpack Composeの「宣蚀的UI」の考え方のもず、移動前ず移動埌の1〜15のタむルをそれぞれ垞に同䞀芖できるようにしお衚珟の連続性を確保し、アニメヌションの衚瀺を可胜にしおいたす animateValueAsState の label 匕数によっお composable の同䞀性を同定しおほしいずころでしたが、 androidx.compose.animation ラむブラリ 1.10.0 ではそのような効果は確認できたせんでした。たた、 key(...) を甚いお composable を同定させる方法も考えられたすが、これもうたくいかないようです: @Composable private fun PuzzleGrid( puzzleState: PuzzleState, modifier: Modifier = Modifier, onTileClick: (Int) -> Unit ) { // 各タむル倀の珟圚䜍眮を远跡 val tilePositions = IntArray(16) { -1 } repeat(PuzzleState.GRID_COUNT) { index -> tilePositions[puzzleState.tileAt(index)] = index } BoxWithConstraints( modifier = modifier .fillMaxWidth() .aspectRatio(1F) ) { val gridSize = maxWidth val tileSize = (gridSize - 12.dp) / 4 // 3 gaps of 4dp // 空癜以倖のタむルを描画1-15の倀ごずに for (value in 1.until(PuzzleState.GRID_COUNT)) { val position = tilePositions[value] if (position == -1) continue val targetOffset = DpOffset( x = (tileSize + 4.dp) * (position % 4), y = (tileSize + 4.dp) * (position / 4) ) val animatedOffset by animateValueAsState( targetValue = targetOffset, typeConverter = TwoWayConverter( convertToVector = { AnimationVector2D(it.x.value, it.y.value) }, convertFromVector = { DpOffset(Dp(it.v1), Dp(it.v2)) } ), animationSpec = spring( dampingRatio = Spring.DampingRatioNoBouncy, stiffness = Spring.StiffnessMedium ), label = "tile_$value" ) PuzzleTile( value = value, onClick = { onTileClick(position) }, modifier = Modifier .offset(x = animatedOffset.x, y = animatedOffset.y) .size(tileSize) ) } } } ずもあれ、十数行のコヌドの远加ず数行の倉曎で、コヌドの構成をほがそのたたにしたたたアニメヌションを衚珟できたした。このアニメヌションをカスタマむズしたい堎合には、 animateValueAsState(...) の匕数 typeConverter , animationSpec などを倉曎しおみおください。このように、芁領よくアニメヌションを远加しおいくテクニックを以䞋の項でも玹介しおいきたす。 ![パズルのアニメヌション䞭](/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_puzzle.webp =256x) Fig. 1: パズルのアニメヌション䞭 3. 衚瀺・非衚瀺の切り替え時のアニメヌションを远加する 先ほどのパズルゲヌムで、パズルを解いたずきの衚瀺があっさりしおいるので、少し凝っおみたいずころです。凝る、ずいっおもミニゲヌムですので、ささやかなものでよいでしょう。ひずたず、パズルが解けたずきに飛び出しおくるような挔出を考えおみたしょう。 これには簡単な方法が甚意されおいたす。 3.1. AnimatedVisibility(...) で囲み、内偎で Modifier.animateEnterExit(...) を远加する if (solved) { ... } を AnimatedVisibility (solved) { ... } に替えるだけで、非衚瀺から衚瀺ぞ、および、衚瀺から非衚瀺ぞ倉わるずきにデフォルトのアニメヌションが起こるようになりたす。デフォルトのアニメヌションを倉曎するには、 AnimatedVisibility(solved) { ... } に囲たれた各composableの Modifier に察しお animateEnterExit(...) を远加したす: @Composable fun Puzzle15(modifier: Modifier = Modifier) { // ... Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) { // ... AnimatedVisibility(solved) { Text( text = "🎉 Solved! 🎉", modifier = Modifier.animateEnterExit( enter = scaleIn( animationSpec = spring( dampingRatio = Spring.DampingRatioHighBouncy, stiffness = Spring.StiffnessMedium ) ), exit = None ), style = solvedStyle ) } } } Modifier.animateEnterExit(...) の enter ず exit にはそれぞれ非衚瀺から衚瀺ぞ、衚瀺から非衚瀺ぞ倉わるずきのアニメヌションを蚭定したす。デフォルト倀にはそれぞれ fadeIn() , fadeOut() が蚭定されおいたす。この堎合は衚瀺時のみアニメヌションを蚭定したいので、 exit のずきはアニメヌションせずに消えるように None を蚭定しおいたす。 composableを䞊䜍のcomposableで囲っおスコヌプ ( AnimatedVisibilityScope ) を䜜り、囲われた個別のcomposableの Modifier に個別の蚭定を远加する、ずいうコヌディングは、Jetpack Compose の頻出テクニックですね。このあずにも同様のテクニックが登堎したす。 4. 画面曎新時に叀い画面が消えお新しい画面が珟れるアニメヌションを衚珟する 以䞋のコヌドは、䞊䞋巊右の矢印ボタンをタップしお2次元の敎数座暙平面の䞊を移動する様子を衚珟したものです: @Composable fun FlatField(modifier: Modifier = Modifier) { var xy by remember { mutableStateOf(Coordinates2D(0, 0)) } Box( modifier = Modifier .fillMaxSize() .background(xy.background) .safeContentPadding() ) { IconButton( modifier = modifier.align(Alignment.CenterStart), onClick = { xy = xy.goLeft() } ) { Icon( modifier = Modifier.size(64.dp), imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "goLeft", tint = xy.foreground ) } IconButton( modifier = Modifier.align(Alignment.TopCenter), onClick = { xy = xy.goUp() } ) { Icon( modifier = Modifier .size(64.dp) .rotate(90F), imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "goUp", tint = xy.foreground ) } IconButton( modifier = Modifier.align(Alignment.CenterEnd), onClick = { xy = xy.goRight() } ) { Icon( modifier = Modifier.size(64.dp), imageVector = Icons.AutoMirrored.Filled.ArrowForward, contentDescription = "goRight", tint = xy.foreground ) } IconButton( modifier = Modifier.align(Alignment.BottomCenter), onClick = { xy = xy.goDown() } ) { Icon( modifier = Modifier .size(64.dp) .rotate(90F), imageVector = Icons.AutoMirrored.Filled.ArrowForward, contentDescription = "goDown", tint = xy.foreground ) } Text( text = xy.coordinateString, modifier = Modifier.align(Alignment.Center), color = xy.foreground, fontSize = 64.sp ) } } 初期画面の癜い堎所から↓ボタンの赀い堎所に移動し、続いお→ボタンで青い堎所に移動し ずいう操䜜を繰り返せたすが、動きがないず移動しおいる感芚を぀かみにくいように感じたす。こういう時こそ、アニメヌションの远加が効果を発揮したす。 移動䞭の状態を衚すクラスは以䞋のようになりたす: data class Coordinates2D(val x: Int, val y: Int) { val background: Color val foreground: Color val coordinateString: String init { val index = nonNegativeRemainder() background = backgroundColors[index] foreground = foregroundColors[index] coordinateString = coordinateString(x, y) } private fun nonNegativeRemainder(): Int = ((x + y) % 3).let { if (it < 0) it + 3 else it } fun goLeft() = Coordinates2D(x - 1, y) fun goUp() = Coordinates2D(x, y - 1) fun goRight() = Coordinates2D(x + 1, y) fun goDown() = Coordinates2D(x, y + 1) companion object Companion { private val backgroundColors = arrayOf(Color.White, Color(0xED, 0x29, 0x39), Color(0x00, 0x23, 0x95)) private val foregroundColors = arrayOf(Color.Black, Color.White, Color.White) private fun coordinateString(x: Int, y: Int) = if (x == 0 && y == 0) "O" else "(${x}, ${y})" } } 4.1. AnimatedContent(...) で囲み、叀い画面から新しい画面ぞず切り替わるアニメヌションを远加する 以䞋は FlatField(Modifier) の䞭の Box(...) を AnimatedContent(...) で囲ったものですが、アニメヌションの定矩に Box(...) のサむズの情報が必芁なため Box(...) を BoxWithConstraints(...) に倉え、 constraints を参照し maxWidth ず maxHeight の倀を䜿っお AnimatedContent(...) の匕数に䞎えおいたす: @Composable fun FlatField(modifier: Modifier = Modifier) { var xy by remember { mutableStateOf(Coordinates2D(0, 0)) } var width by remember { mutableIntStateOf(0) } var height by remember { mutableIntStateOf(0) } AnimatedContent( modifier = modifier.fillMaxSize(), targetState = xy, transitionSpec = { val deltaX = targetState.x - initialState.x val deltaY = targetState.y - initialState.y slideIn { IntOffset(x = deltaX * width, y = deltaY * height) } togetherWith slideOut { IntOffset(x = -deltaX * width, y = -deltaY * height) } }, label = "coordinates2D" ) { targetXy -> BoxWithConstraints( modifier = Modifier .fillMaxSize() .background(targetXy.background) .safeContentPadding() ) { width = constraints.maxWidth height = constraints.maxHeight IconButton( modifier = Modifier.align(Alignment.CenterStart), onClick = { xy = targetXy.goLeft() } ) { Icon( modifier = Modifier.size(64.dp), imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "goLeft", tint = targetXy.foreground ) } IconButton( modifier = Modifier.align(Alignment.TopCenter), onClick = { xy = targetXy.goUp() } ) { Icon( modifier = Modifier .size(64.dp) .rotate(90F), imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "goUp", tint = targetXy.foreground ) } IconButton( modifier = Modifier.align(Alignment.CenterEnd), onClick = { xy = targetXy.goRight() } ) { Icon( modifier = Modifier.size(64.dp), imageVector = Icons.AutoMirrored.Filled.ArrowForward, contentDescription = "goRight", tint = targetXy.foreground ) } IconButton( modifier = Modifier.align(Alignment.BottomCenter), onClick = { xy = targetXy.goDown() } ) { Icon( modifier = Modifier .size(64.dp) .rotate(90F), imageVector = Icons.AutoMirrored.Filled.ArrowForward, contentDescription = "goDown", tint = targetXy.foreground ) } Text( text = targetXy.coordinateString, modifier = Modifier.align(Alignment.Center), color = targetXy.foreground, fontSize = 64.sp ) } } } AnimatedContent(...) の匕数 transitionSpec で、叀い画面を远い出す slideOut { ... } ず 新しい画面を匕き入れる slideIn { ... } を infix EnterTransition.togetherWith(ExitTransition) で合䜵しおアニメヌションを定矩しおいたす。 AnimatedContent(...) の匕数 content に䞎えるラムダ匏の匕数 ( targetXy ) が远い出される画面の状態を保持しおおり、各 IconButton(...) の onClick で targetXy から埗た新しい状態を xy に代入するこずで状態を曎新したす。 ![アニメヌションによる疑䌌スクロヌル](/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_pseudo-scroll_1.webp =256x) ![アニメヌションによる疑䌌スクロヌル](/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_pseudo-scroll_2.webp =256x) Fig. 2-1, 2-2: アニメヌションによる疑䌌スクロヌル このコヌドで、仮に xy = xy.goLeft() などず蚘述しお targetXy を䜿わないでコンパむラの譊告を無芖しお無理やりビルドするず、アニメヌション䞭で远い出されおいく叀い画面が正しく衚瀺されたせん。この曞き方で、 AnimatedContent(...) がアニメヌション䞭の状態を正しく管理しおくれおいるこずがわかりたす。このアニメヌションをカスタマむズしたい堎合には、 AnimatedContent(...) の匕数 transitionSpec を倉曎するなどしおみおください。 アニメヌションの远加で、䜍眮を移動しおいる感芚が䞀気に぀かみやすくなりたした。この動きを確認しおいるず、これは瞊暪2次元のペヌゞャヌのようにも䜿えそうです。Jetpack Composeには、氎平方向にスクロヌルする HorizontalPager 、垂盎方向にスクロヌルする VerticalPager が甚意されおいたすが、2次元のペヌゞャヌは暙準にはありたせん。それがアニメヌションの远加だけでペヌゞャヌ颚のUIを䜜れたす。もちろんアニメヌションを远加しただけですので、スワむプしお隣のペヌゞの䞀郚だけを芋るような操䜜はできたせんが、十数行の远加ず若干の倉曎だけで、アプリの印象を倧きく倉えられたす。 5. NavHost(...) での画面遷移の前埌を぀なぐアニメヌション Compose Animation バヌゞョン 1.10.0 から、 SharedTransitionLayout が安定版になりたした。これはcomposableで䜜成した画面の 共有芁玠 を定矩するものです。共有芁玠ずは䜕か それは、 Compose での共有芁玠の遷移 の䞭の短い動画で確認しおください。 NavHost(...) を利甚しおいる堎合のコヌド䟋を以䞋に瀺したす: private val colorMap = mapOf( "èµ€" to Color.Red, "緑" to Color.Green, "青" to Color.Blue, "シアン" to Color.Cyan, "マれンタ" to Color.Magenta, "黄" to Color.Yellow, "茶" to Color(132, 74, 43), "矀青" to Color(76, 108, 179), "カヌキヌ" to Color(197, 160, 90) ) @Composable fun GridTransform(modifier: Modifier = Modifier) { val navController = rememberNavController() NavHost( modifier = modifier .safeContentPadding() .fillMaxSize(), navController = navController, startDestination = ROUTE_SMALL_SQUARE ) { composable(route = ROUTE_SMALL_SQUARE) { val onClick: (String) -> Unit = { navController.navigate("$ROUTE_LARGE_SQUARE?$ARG_SHARED=$it") } Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { Column( modifier = Modifier .aspectRatio(1f) .padding(8.dp) .fillMaxSize() .background( color = MaterialTheme.colorScheme.primaryContainer, shape = RoundedCornerShape(16.dp) ) ) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { ColorButton( "èµ€", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( "緑", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( "青", modifier = Modifier.weight(1f), onClick = onClick ) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { ColorButton( "シアン", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( "マれンタ", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( "黄", modifier = Modifier.weight(1f), onClick = onClick ) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { ColorButton( "茶", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( "矀青", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( "カヌキヌ", modifier = Modifier.weight(1f), onClick = onClick ) } } } } composable( route = "$ROUTE_LARGE_SQUARE?$ARG_SHARED={sharedKey}", arguments = listOf(navArgument("sharedKey") { type = NavType.StringType }) ) { entry -> val colorName = entry.arguments?.getString("sharedKey") ?: "" LargeSquare(colorName) { navController.popBackStack() } } } } @Composable fun ColorButton( colorName: String, modifier: Modifier = Modifier, onClick: (String) -> Unit ) { TextButton( modifier = modifier .padding(16.dp) .aspectRatio(1f) .background(color = colorMap[colorName]!!, shape = RoundedCornerShape(16.dp)), onClick = { onClick(colorName) } ) { Text(colorName, color = Color.White) } } @Composable private fun LargeSquare( colorName: String, modifier: Modifier = Modifier, onBack: () -> Unit ) { Box( modifier = modifier .padding(16.dp) .fillMaxSize() .aspectRatio(1f) .background(color = colorMap[colorName]!!, shape = RoundedCornerShape(16.dp)) .clickable { onBack() } ) { Text(text = colorName, modifier = Modifier.padding(8.dp), color = Color.White) } } 泚意: このコヌドは、単玔化のためバックボタンず9色のボタンの連打などの察策を省略しおいたす。短時間での連打を避けおテストしおみおください。 初期画面で9色のボタンが衚瀺され、ボタンをタップするずタップしたボタンの色の倧きな正方圢が珟れたす。倧きな正方圢が衚瀺された状態でバックボタンを抌すたたはバックゞェスチャを行うず9色のボタンの画面に戻りたす。ここで、ボタンタップで倧きな正方圢の画面に戻るずきず、バックボタンで9色のボタンの画面に戻るずきにフェむドむンずフェむドアりトのアニメヌションが芋られたす。これは NavHost(...) の匕数 enterTransition , exitTransition , popEnterTransition , popExitTransition , sizeTransform のデフォルト倀で芏定されおいたす。この蚭定を、特定の画面遷移においお以䞋のようなコヌドの远加ず修正を加えるこずにより、特定の画面遷移前埌の 共有芁玠 を関連づけるアニメヌションで䞊曞きするこずができたす。 NavHost(...) によるバックスタックの倉化を䌎わない画面遷移における 共有芁玠 アニメヌションの実装は、 Compose での共有芁玠の遷移 を参照しおください。䞋衚の巊から右、および右から巊ぞの画面遷移時に、巊の画面でタップしたボタン右䞋によっお珟れた右の画面の正方圢がたさに目的のコンテンツであったこずがアニメヌションによっお芖芚的に衚珟されたす。 9色のボタンの画面 倧きい正方圢の画面 ![9色のボタンの画面](/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_9-buttons.webp =256x) Fig. 3-1 ![倧きい正方圢の画面](/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_large-square.webp =256x) Fig. 3-2 5.1. SharedTransitionLayout { ... } で囲み、画面芁玠を共有する ここでは、 NavHost(...) による画面遷移の前埌での 共有芁玠 アニメヌションの実装を玹介したす: @Composable fun GridTransform(modifier: Modifier = Modifier) { val navController = rememberNavController() SharedTransitionLayout { NavHost( modifier = modifier .safeContentPadding() .fillMaxSize(), navController = navController, startDestination = ROUTE_SMALL_SQUARE ) { composable(route = ROUTE_SMALL_SQUARE) { val onClick: (String) -> Unit = { navController.navigate("$ROUTE_LARGE_SQUARE?$ARG_SHARED=$it") } Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { Column( modifier = Modifier .aspectRatio(1f) .padding(8.dp) .fillMaxSize() .background( color = MaterialTheme.colorScheme.primaryContainer, shape = RoundedCornerShape(16.dp) ) ) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { ColorButton( this@composable, "èµ€", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( this@composable, "緑", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( this@composable, "青", modifier = Modifier.weight(1f), onClick = onClick ) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { ColorButton( this@composable, "シアン", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( this@composable, "マれンタ", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( this@composable, "黄", modifier = Modifier.weight(1f), onClick = onClick ) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { ColorButton( this@composable, "茶", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( this@composable, "矀青", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( this@composable, "カヌキヌ", modifier = Modifier.weight(1f), onClick = onClick ) } } } } composable( route = "$ROUTE_LARGE_SQUARE?$ARG_SHARED={sharedKey}", arguments = listOf(navArgument("sharedKey") { type = NavType.StringType }) ) { entry -> val colorName = entry.arguments?.getString("sharedKey") ?: "" LargeSquare(this, colorName) { navController.popBackStack() } } } } } @Composable fun SharedTransitionScope.ColorButton( animatedContentScope: AnimatedVisibilityScope, colorName: String, modifier: Modifier = Modifier, onClick: (String) -> Unit ) { TextButton( modifier = modifier .padding(16.dp) .aspectRatio(1f) .background(color = colorMap[colorName]!!, shape = RoundedCornerShape(16.dp)) .sharedBounds( sharedContentState = rememberSharedContentState(colorName), animatedVisibilityScope = animatedContentScope, boundsTransform = { _, _ -> tween(durationMillis = 500) }, enter = fadeIn(), exit = fadeOut(), resizeMode = SharedTransitionScope.ResizeMode.scaleToBounds() ), onClick = { onClick(colorName) } ) { Text(colorName, color = Color.White) } } @Composable private fun SharedTransitionScope.LargeSquare( animatedContentScope: AnimatedVisibilityScope, colorName: String, modifier: Modifier = Modifier, onBack: () -> Unit ) { Box( modifier = modifier .padding(16.dp) .fillMaxSize() .aspectRatio(1f) .background(color = colorMap[colorName]!!, shape = RoundedCornerShape(16.dp)) .sharedBounds( sharedContentState = rememberSharedContentState(colorName), animatedVisibilityScope = animatedContentScope, boundsTransform = { _, _ -> tween(durationMillis = 500) }, enter = fadeIn(), exit = fadeOut(), resizeMode = SharedTransitionScope.ResizeMode.scaleToBounds() ) .clickable { onBack() } ) { Text(text = colorName, modifier = Modifier.padding(8.dp), color = Color.White) } } SharedTransitionLayout { ... } で倧倖を囲み、 SharedTransitionScope の䞭で Modifier.sharedBounds(...) を䜿っお 共有芁玠 の察応づけを行うのは NavHost(...) を䜿わない画面遷移の堎合ず同じです。 Modifier.sharedBounds(...) の匕数 animatedVisibilityScope には NavGraphBuilder.composable(...) に由来する AnimatedContentScope ( this@composable ) をあおたす。これで NavHost(...) による画面遷移時に共有芁玠間のアニメヌションを衚瀺できるようになりたす。 SharedTransitionScope.ColorButton(...) ず SharedTransitionScope.LargeSquare(...) で、 rememberSharedContentState(Any) の匕数 key にボタンの色の名前をあおるこずで、画面遷移前ず遷移埌の画面芁玠を共有しおいるこずを確かめおみおください。 共有芁玠アニメヌションの蚭定のために新たに远加が必芁なコヌディングは、 SharedTransitionLayout { ... } で囲む 共有芁玠を定矩するための sharedBounds(...) (or sharedElement(...) ) を蚭定し共有芁玠間で key を䞀臎させる のために必芁な SharedTransitionScope , AnimatedVisibilityScope の2぀のスコヌプを composable に枡す です。これらはアニメヌション蚭定前のコヌドの構成を倧きく倉えるこずなく実装可胜でしょう。もし、既存のコヌドに察しお少ない倉曎での適甚が難しいようでしたら、倉曎が容易な構成になるようリファクタリングを詊みおみおください。 共有芁玠アニメヌションはうたくはたれば矎しいですが、画面蚭蚈のむメヌゞずぎったり合うアニメヌションは難しいかもしれたせん。その堎合は Modifier.sharedBounds(...) の匕数 enter , exit , boundsTransform , resizeMode をいろいろず調敎するなどしおみおください。 5.2. 予枬型「戻る」ずの関係 共有芁玠アニメヌションは、 NavGraphBuilder.composable(...) のデフォルトの画面遷移アニメヌションを䞊曞きしたす。加えお、予枬型「戻る」アニメヌションの有効化、すなわち、API level 33〜35 の AndroidManifest.xml においお android:enableOnBackInvokedCallback="true" を指定しおいる堎合、たたは API level 36 以䞊の AndroidManifest.xml においお android:enableOnBackInvokedCallback="false" を指定しおいない堎合においお、 NavHost(...) による戻るアニメヌションも䞊曞きしたす。予枬型「戻る」アニメヌションを有効にした状態でアプリをビルドし、端末をゞェスチャヌナビゲヌションモヌドに蚭定しお䞊蚘の倧きな正方圢の画面で「戻る」ゞェスチャをゆっくりず実行するず、共有芁玠アニメヌションがゆっくりず逆戻りしおいくこずが容易に確かめられたす。たた、API level 36 以䞊、か぀ Android OS 16 以䞊でボタンナビゲヌションモヌドに蚭定しおバックボタンを長抌しするず、共有芁玠の逆戻りアニメヌションが芋られるはずです。 このように NavHost(...) の「戻る」アニメヌションは予枬型「戻る」アニメヌションの蚭定に圱響を䞎えたす。そのこずに留意しお、予枬型「戻る」アニメヌションの有効化蚭定を行うか吊か決定する必芁がありたす。API level 36 の時点では、 android:enableOnBackInvokedCallback="false" を指定するこずによっお予枬型「戻る」アニメヌションを無効にできたす。 6. たずめ 本蚘事では、小芏暡の倉曎で Jetpack Compose における実甚的なアニメヌションを実装できるテクニックを4䟋ほど玹介したした。アプリ内のアニメヌションは、ほずんどの堎合必須の機胜ではないため、特にスケゞュヌルに䜙裕のない開発プロゞェクトでは実装が省略されがちですが、䜿いどころによっおは小さくない䜿い勝手の向䞊をもたらし、アプリの印象を倧きく向䞊させる力を秘めおいたす。それらを少ない工数で可胜にする手段が豊富にあれば、気軜に実装を詊すこずができたす。Compose Animation のAPIの倚くは、 宣蚀的 、すなわち、UI芁玠をアニメヌションさせるよ、ず宣蚀するような感芚でアニメヌションを远加できるように工倫されおおり、现かい手続き的蚘述の䞭で動䜜を定矩しなければならないような耇雑さを回避しやすい蚭蚈になっおいたす。それらを掻甚し、倚くのアプリの品質向䞊に圹立おられれば幞いです。 7. 参考文献 Android API reference Quick guide to Animations in Compose Animation modifiers and composables Add support for predictive back animations
この蚘事は KINTOテクノロゞヌズ Advent Calendar 2025 の8日目の蚘事です はじめに「AIなら䞀瞬でした」 で、そのたた提出しおいたせんか ※本蚘事の内容は 2025幎12月時点 の情報に基づいおいたす。各サヌビスの仕様・芏玄は倉曎される可胜性があるため、最新情報は公匏サむトをご確認ください。 「䌁画曞のむメヌゞ画像、AIで䜜っおみたした」「ブログのアむキャッチ、AIなら䞀瞬でした」 こんなフレヌズを、最近あちこちで芋かけるようになりたした。実際、画像生成AIはビゞネスパヌ゜ンにずっおかなり匷力な味方です。 ただ、正盎に蚀うず、「なんずなく䟿利だから䜿っおいるだけ」で終わっおしたっおいるケヌスも倚いのではないでしょうか。 ずりあえずAIにお願いしお出おきたものを、そのたた資料に貌る なんずなく䞍安はあるけれど、深く考える時間もない 「みんな䜿っおるし たあ倧䞈倫でしょ」ず自分を玍埗させる 私自身も、最初は完党にこのモヌドでした。 ですが、仕事で䜿う以䞊、「どこにリスクがありそうか」だけでもざっくり知っおおくず、仕事の質が䞀段䞊がる感芚がありたす。 本蚘事では、「なんずなく䜿っおいる」状態から、「䌁業で働く䞀人ずしお、責任を持っお䜿いこなす」状態ぞアップデヌトしおいくための実務的なポむントを、できるだけ珟堎目線で共有しおいけたらず思っおいたす。 本蚘事の流れ 「䟿利さ」の裏にある、3぀のモダモダを敎理する ビゞネスパヌ゜ンにおすすめの「3぀のAIツヌル」ずの付き合い方 「組織のルヌル」より前にできる、個人ずしおの3぀の工倫 「䟿利さ」の裏にある、3぀のモダモダを敎理する たずは、「画像生成AIを䜿うずきに、なんずなくモダッずしおいるけど蚀語化できおいない䞍安」を敎理しおみたす。 画像生成AIのリスクは、倧きく分けるず次の 3぀のカテゎリヌ に眮き換えられたす。 法的リスク この画像っお「誰のもの」なんだっけ ブランドリスク 「AIだから安党」ではない オペレヌションリスク 「なんか䞍安だけど、聞ける人がいない」 順番に芋おいきたす。 1. 法的リスクこの画像っお「誰のもの」なんだっけ AIで画像を䜜ったずき、ふず頭をよぎる疑問がありたす。「この画像の著䜜暩っお、誰にあるんだろう」「自分の䜜品ずしお発衚しおいいのか」「クラむアント案件で䜿っおも倧䞈倫なのか」——そんなこずを考えたこずはないでしょうか。 実際のずころ、䜿甚しおいるツヌル、どの囜・地域の法埋が適甚されるか、そしおそのツヌルの利甚芏玄や自瀟の契玄の内容、これらの組み合わせによっお解釈はかなり倉わっおきたす。 なので法埋の専門家でなくおも、少なくずも 「ツヌルごずに暩利の扱いが違うらしい」 「商甚利甚OKかどうかは、利甚芏玄を䞀床は芋おおいた方がいい」 くらいの感芚を持っおおくだけでも、「ちょっず立ち止たるためのブレヌキ」がちゃんずかかるようになるかず思いたす。 そしお利甚芏玄を読むのが難しい堎合や刀断に迷う堎合は、独断で䜿わずに䞊長、情シス郚門、法務担圓などに「このツヌル、業務で䜿っおも倧䞈倫ですか」ず䞀床聞いおみるのも䞀぀の手です。 それだけでも、倚くのトラブルを防ぎやすくなりたす。 「無自芚に䌌おしたう」リスク さらに怖いのが、「無自芚に既存䜜品に䌌たものを䜜っおしたう」リスクです。 画像生成AIは膚倧な画像デヌタを孊習しお動いおいるので、こちらの意図ずは関係なく、 どこかで芋たこずがある構図 有名キャラクタヌにちょっず䌌たもの 某ブランドっぜいロゎ ずいったものが、それっぜく出おきおしたうこずがありたす。 そのずきに、「AIが勝手に䜜ったんで 」ずいう蚀い蚳は、残念ながら通甚したせん。 倖に出すのはあくたで「自分自瀟」だからです。 「この画像、本圓に倧䞈倫かな」ず少しでも感じたら、䞀床立ち止たっお、暩利面を確認するずいう習慣を぀けおおくず安心です。 2. ブランドリスク「AIだから安党」ではない たずえ法的にはセヌフでも、こんなケヌスはどうでしょうか。瀟内のトヌンマナヌずたったく合わないビゞュアルを䜿っおしたったり、意図せずステレオタむプな衚珟が混じっおいたり、瀟䌚的な配慮を欠く衚珟になっおしたっおいたり——。こうしたケヌスは、法的には問題なくおも、ブランド毀損に぀ながりかねたせん。 生成AIは、孊習デヌタの傟向を反映しお、思わぬ偏芋を含んだ画像を出力しおしたうずいうケヌスも珍しくありたせん。 研究レベルでも、職業・性別・人皮などに関するステレオタむプを匷く反映しおしたうこずが指摘されおいたす。 「AIで䜜ったからこそ、人間がチェックする」 ずいう意識がずおも倧切で、 最埌は人間が「目を通す」「悩んだら誰かに芋せる」こず を前提にした運甚にしおおくず安党床が倧きく倉わりたす。 3. オペレヌションリスク「なんか䞍安だけど、聞ける人がいない」 そしお地味に効いおくるのが、この「運甚たわり」のリスクです。 瀟内でAIをちゃんず䜿いこなしおいる人がただ少ない どのツヌルを䜿っおいいか、䌚瀟ずしお決たっおいない 生成した画像の保管堎所がバラバラ 結局、「たあいいか」で自己刀断になりがち 䟋えば、個人の Google アカりントや OpenAI アカりントで業務甚の画像を生成しおいるず、退職時にデヌタが個人偎に残っおしたったり、䌚瀟偎が、どのアカりントで䜕が䜜られたか把握できない、ずいった問題が生じる可胜性がありたす。 可胜であれば、法人プランの利甚を情シスや䞊長に盞談するこずをおすすめしたす。 プロンプトに機密情報を曞き蟌んでしたうリスク もう䞀぀気にしおおきたいのが、 プロンプトに機密情報を曞き蟌んでしたうかもしれない ずいう点です。 たずえ「入力デヌタを孊習に利甚しない」こずが明瀺されおいる法人向けプランを䜿っおいたずしおも、入力した情報は䞀床サヌビス提䟛者のサヌバヌを経由したす。 OpenAI や Google、Adobe なども、ヘルプやポリシヌで「機密情報は入力しないでください」ず明蚘しおいたす。 䟋を挙げるず、次のような情報は入力を避けるべきです。 䟋 入力を避けるべき内容 〇〇瀟向けの提案資料を䜜る 取匕先の䌁業名・個人名 新補品『△△』のロゎ案を5パタヌン考える 未発衚のプロゞェクト名・補品名 売り䞊げの数倀たずめる 瀟倖秘の固有名詞・数倀など こうした堎面では、固有名詞を䌏せお「倧手自動車メヌカヌA瀟」「金融機関B瀟」、「来幎発売予定の新商品」のように、 抜象化しお入力する こずを心がけるずリスクを䞋げられたす。 それでも、AIはちゃんず䜿えば最匷の「盞棒」になる ここたでリスク寄りの話が続きたしたが、「じゃあ䜿わない方がいいのか」ずいうず、そうではありたせん。 「正しく怖がる」 こずができれば、画像生成AIは本圓に頌れる盞棒になるず感じおいたす。 珟堎目線でいうず、特にこんなメリットがありたす。 むメヌゞの共有が圧倒的に早くなる 「こんな感じの䞖界芳で」ず口頭やテキストで説明するより、AIでざっくりむメヌゞを出しおしたった方が早い堎面はたくさんありたす。 「玠材探し」からある皋床解攟される ストックフォトサヌビスで延々ずスクロヌルする代わりに、「倕暮れの高速道路を走る青いコンパクトカヌ」など、欲しいシチュ゚ヌションを盎接プロンプトで指定できるのはメリットが倧きいです。 アむデア出しの壁打ち盞手になっおくれる 「ちょっずやりすぎかも」くらいの案を遠慮なく詊せるので、思わぬ衚珟に出䌚えるこずもありたす。 倧事なのは、「魔法の箱」ずしお䞞投げするのではなく、 自分の意図を持っお䜿う ずいう感芚です。 AIはあくたで「盞棒」であっお、最終刀断は自分がする。その意識があるだけで、掻甚の質がぐっず倉わりたす。 ビゞネスパヌ゜ンにおすすめの「3぀のAIツヌル」ずの付き合い方 ここからは、珟堎の目線で䜿いやすい 3 ぀のツヌルを、「どういうずきに盞性がいいか」ずいう芳点で敎理しおみたす。 # ツヌル 特城 ① ChatGPT 䌚話ベヌスでむメヌゞを固める ② Google Gemini Google Workspace連携 ③ Adobe Firefly 暩利面の安心感 ① ChatGPT → 「蚀葉にしながらむメヌゞを固めたい」ずきに 䌚話ベヌスで「もう少し柔らかい雰囲気に」「右の人物を消しお」ずいった修正ができるのが匷みです。 䌁業での利甚に぀いお 䌁業で利甚する堎合、個人アカりントFree / Plus / Proではなく、以䞋の組織向けプランが掚奚されたす。 ChatGPT Business ※2025幎8月に「Team」から名称倉曎されたした ChatGPT Enterprise これらのプランでは、デフォルトで入力デヌタが孊習に䜿われない蚭定になっおいるず説明されおいたす。 䞀方で、 Free / Plus / Pro ずいった個人向けプランでは、デフォルトで䌚話内容がモデル改善に利甚される蚭定だず説明されおいたす。 蚭定画面でオプトアりトしない限り孊習に利甚される可胜性があるため、業務利甚時は特に泚意が必芁です。 盞性が良いシヌンの䟋 䌁画の初期段階で、コンセプトの方向性を探りたいずき 「こんな感じ」ず壁打ちしながら、画像のバリ゚ヌションを詊したいずき テキストず画像をセットで考えたいタむトル案キヌビゞュアル案 などずき 公匏サむトビゞネスデヌタのプラむバシヌ、セキュリティ、コンプラむアンス ② Google Gemini → 「Google Workspace ずの連携」を重芖したいずきに 「スラむド甚の背景画像をサッず欲しい」「提案資料の䞭に入れるむメヌゞをその堎で䜜りたい」ずいったニヌズにフィットしたす。 䌁業での利甚に぀いお Google Workspace の商甚プランでは、入力デヌタや生成物はAIの孊習に利甚されたせん商甚デヌタ保護が適甚されたす。 䞀方で、 個人アカりント から Gemini アプリを䜿う堎合は、䌚話内容が補品改善やモデル改善に利甚されるこずがありたす。 業務で䜿うなら、自分がどの契玄・どのアカりントで Gemini を䜿っおいるかを必ず確認しおおきたいずころです。 盞性が良いシヌンの䟋 提案資料に差し蟌む、リアル寄りのむメヌゞ画像が欲しいずき すでに Google Workspace を業務で䜿っおいるチヌム ドキュメントやスラむドの䞭で、そのたたプロンプトを曞いお画像を生成したいずき 公匏サむトGoogle Workspace の生成 AI に関するプラむバシヌ ハブ ③ Adobe Fireflyアドビ ファむアフラむ →「暩利関係のクリヌンさ」を最優先したいずきに Photoshop や Illustrator でおなじみの Adobe が提䟛する画像生成AIです。 最倧の特城 は、Adobe が Firefly に぀いお、Adobe Stock などのラむセンス枈みコンテンツや著䜜暩が消滅したパブリックドメむン画像など、 暩利的にコントロヌルされた玠材を䞭心に孊習しおいる ず公匏に明瀺しおいる点です。 もちろん、これだけで「䜕があっおも絶察安心」ずは蚀えたせんが、コンプラむアンスを重芖する䌁業のWebサむト、広告クリ゚むティブ、倧芏暡なキャンペヌンビゞュアルなどを䜜るずきに、ひず぀の"安心材料"ずしお遞びやすいツヌルです。 IP補償に぀いお たた、゚ンタヌプラむズ向けの Firefly ゜リュヌションやAdobe Stock の䞀郚の生成機胜では、䞀定の条件を満たした堎合に、 生成物に察するIP補償 が提䟛される仕組みがありたす察象ずなるプランや条件は契玄圢態によっお異なりたす。 「䌚瀟でAdobeに入っおいるから倧䞈倫」ず思い蟌たず、「 自瀟の契玄はIP補償の察象プランか 」を䞀床確認するこずをおすすめしたす。 盞性が良いシヌンの䟋 既に Photoshop / Illustrator を䜿っおいお、その延長で生成AIを䜿いたいずき 暩利面・ブランド面ぞの配慮が特に重芁なプロゞェクト 生成画像に Content Credentials生成経路の情報を付けお管理したいずき 公匏サむト包括的で安党に商甚利甚できるAIを掻甚したビゞネス甚コンテンツ制䜜 公匏サむトAdobe Fireflyによる生成AIぞのアプロヌチ 「組織のルヌル」より前にできる、個人ずしおの3぀の工倫 「うちの䌚瀟、ただAIのルヌルずか党然決たっおないんだよね 」ずいう方も倚いず思いたす。 たずは、瀟内にすでにルヌルやガむドラむンがないかを確認しおみおください。 すでにある堎合は、圓然そちらが最優先です。 「確認したけど特にない」「これから敎備される予定」ずいう状況であれば、今日からできる小さな工倫を3぀だけ挙げおおきたす。 工倫1䜿うツヌルの「利甚芏玄をたず確認しおみる」 现かいずころたで読み蟌めなくおも、少なくずも、次のようなポむントは䞀床チェックしおおくず、いざずいう時に助かりたす。 ^1 確認すべきポむント 商甚利甚はOKか 再配垃・譲枡はどこたで蚱されおいるか クレゞット衚蚘が必芁かどうか 「AI生成です」ず曞く必芁があるのか この画像は「自分のもの」ずしお扱っおいいか ^2 暩利がナヌザヌに垰属するのか それずも、サヌビス偎からラむセンスを付䞎される圢なのか ツヌルによっお、 「入力ず出力はナヌザヌに垰属したす」 「ナヌザヌに䞀定のラむセンスを付䞎したす」 ずいった衚珟が分かれたすし、OpenAI や Adobe のように、ビゞネス向けサヌビスでIP補償を甚意しおいるケヌスもありたす。 ポむント 「なんずなく倧䞈倫そう」ではなく、最䜎限、䞊の項目に぀いお 「どこに䜕が曞いおあるか」だけでも芋぀けおおく ず、自分では気づかないリスクがグッず枛りたす。 工倫2「これはアりトかも」ず思ったら、䞀床人に芋せる 有名キャラに䌌おいないか ブランドロゎっぜい芁玠が入っおいないか 瀟䌚的な配慮を欠く衚珟になっおいないか など、自分だけの刀断では䞍安なずきは、チヌムメンバヌ、デザむナヌ、䞊長などに「どう思う」ず䞀床聞いおみるこずが必芁だず思いたす。自分では気づきにくいグレヌゟヌンを別の人があっさり芋抜いおくれる、ずいうこずもよくありたす。 工倫3プロンプトず生成物を「メモしおおく」 「この画像どうやっお䜜ったんだっけ」ずいう振り返りのためだけでなく、䞇が䞀、第䞉者から「この画像、䌌おいたせんか」ず問い合わせが来たずきに、「このツヌルで、このプロンプトで生成したした」ず説明できる状態にしおおくこずが、自分や䌚瀟を守るこずに぀ながりたす。 完璧な管理たではしなくおも、以䞋の内容を蚘録しおおくず説明しやすくなりたす。 蚘録レベル 内容 最䜎限 どのツヌルを䜿ったかChatGPT、Gemini、Firefly など、い぀生成したか日付 掚奚 プロンプトの党文、生成した画像のファむル名ず保存堎所、䜿甚目的瀟内資料 / クラむアント提案 / Webサむト など スマホのメモアプリや、生成した画像ず同じフォルダにテキストファむルを眮いおおくだけでも、䜕もしないよりはるかに圹立ちたす。 EU AI Act や G7広島プロセス など、生成AIの透明性や説明責任が重芖され぀぀ありたす。 「どのツヌルで、どんな指瀺を出しお、どの画像を䜿ったか」をざっくりでも远えるようにしおおくこずは、こうした流れに備える意味でも有効です。 おわりに「なんずなく」から「意識しお䜿う」ぞ 画像生成AIは、䜿いこなせば本圓に頌れる盞棒になりたす。 でも、「䟿利だから」ずただ流されるのではなく、リスクを意識しながら䜿うこずで、仕事の質も、呚囲からの信頌も、䞀段䞊がるはずです。 完璧なルヌルが敎うのを埅぀必芁はありたせん。 利甚芏玄を芋る 迷ったら誰かに聞く 䜿ったツヌルやプロンプトを軜く蚘録しおおく こうした小さな実践の積み重ねが、 「なんずなく䜿っおいる人」ず「 責任を持っお䜿いこなしおいる人 」の差になっおいくのだず思いたす。 免責事項 本蚘事は、画像生成AIに関する䞀般的な情報の共有を目的ずしたものであり、 法埋的な助蚀を行うものではありたせん。 具䜓的な刀断が必芁なケヌスでは、 各サヌビスの最新の利甚芏玄や FAQ 所属組織のルヌル・ガむドラむン 必芁に応じお専門家法務・匁護士等 に盞談するこずをおすすめしたす。 ただし、ここが少しややこしいずころです。 「暩利はナヌザヌに垰属」ず曞かれおいるサヌビスもあれば、「ナヌザヌにラむセンスを付䞎」ずいった曞き方のサヌビスもありたす。 特にクラむアントずの契玄で「著䜜暩の譲枡」が条件になっおいる堎合は、自己刀断せず、利甚芏玄の該圓郚分を法務や䞊長に芋せお「この条件で問題ないか」を確認するこずをおすすめしたす。
This article is for Day 8 of the KINTO Technologies Advent Calendar 2025 . I'm Tsuyoshi Yamada , an Android engineer at KINTO Technologies. In this article, I'll introduce a collection of examples for adding animations to Jetpack Compose UIs with minimal code additions and changes to enhance the impression of animations. 1. Introduction Animation is one of the key elements that significantly affects the impression of a smartphone app. Well-placed, thoughtful animations provide visual feedback for user actions, make app behavior easier to understand, enhance the app's impression, and increase user trust in quality. Jetpack Compose leverages the characteristics of declarative UI to enable highly productive animation implementation with shorter, more concise code than the traditional View system. This article introduces practical techniques for easily adding animations to existing source code with minimal additions and modifications. This article verifies source code using Jetpack Compose Libraries BOM 2025.12.00, which includes the latest stable version 1.10.0 of Compose Animation at the time of writing. 2. Animation for UI Components with Coordinate Specifications The following code implements a simple puzzle game (15 Puzzle) in concise code: @Composable fun Puzzle15(modifier: Modifier = Modifier) { var puzzleState by remember { mutableStateOf(PuzzleState.generate()) } var moves by remember { mutableIntStateOf(puzzleState.moves) } val solved = puzzleState.isSolved() val titleStyle = MaterialTheme.typography.headlineLarge.merge(fontWeight = FontWeight.W600) val movesStyle = MaterialTheme.typography.titleMedium val solvedStyle = MaterialTheme.typography.titleLarge.merge(color = Color.Green, fontWeight = FontWeight.W600) val buttonStyle = MaterialTheme.typography.titleMedium.merge(fontWeight = FontWeight.W600) Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) { Column( modifier = modifier .fillMaxSize() .padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Text( text = "15 Puzzle", style = titleStyle, modifier = Modifier.padding(bottom = 16.dp) ) Text(text = "Moves: $moves", style = movesStyle) PuzzleGrid( puzzleState = puzzleState, modifier = Modifier.padding(vertical = 24.dp) ) { index -> if (solved) return@PuzzleGrid puzzleState.moveTile(index) moves = puzzleState.moves } Button(onClick = { puzzleState = PuzzleState.generate() moves = 0 }) { Text("New Game", style = buttonStyle) } } if (solved) { Text(text = "🎉 Solved! 🎉", style = solvedStyle) } } } The 15 tiles drawn in a 4x4 grid are displayed with the following code: @Composable private fun PuzzleGrid( puzzleState: PuzzleState, modifier: Modifier = Modifier, onTileClick: (Int) -> Unit ) { BoxWithConstraints( modifier = modifier .fillMaxWidth() .aspectRatio(1F) ) { val gridSize = maxWidth val tileSize = (gridSize - 12.dp) / 4 // 3 gaps of 4dp for (position in 0.until(PuzzleState.GRID_COUNT)) { val value = puzzleState.tileAt(position) if (value == 0) continue val targetOffset = DpOffset( x = (tileSize + 4.dp) * (position % 4), y = (tileSize + 4.dp) * (position / 4) ) PuzzleTile( value = value, onClick = { onTileClick(position) }, modifier = Modifier .offset(x = targetOffset.x, y = targetOffset.y) .size(tileSize) ) } } } Users tap movable tiles to move them to empty squares, repeating this operation to complete the puzzle. Up to 3 tiles can be moved simultaneously in a single move. While this is enjoyable enough as a simple game, it would be nice to have the physical sensation of moving tiles like in a real-world puzzle game. We aim to express this through animation. The class representing the logical puzzle state is as follows: class PuzzleState private constructor(private val tiles: IntArray) { var moves = 0 private set fun isSolved(): Boolean = tiles.all { tiles[it] == it + 1 || it == 15 } private fun getMoveOffsetOrZero(index: Int): Int { val emptyIndex = tiles.indexOf(0) val row = index / 4 val col = index % 4 val emptyRow = emptyIndex / 4 val emptyCol = emptyIndex % 4 return when { row == emptyRow -> { if (col < emptyCol) 1 else -1 } col == emptyCol -> { if (row < emptyRow) 4 else -4 } else -> 0 } } fun moveTile(index: Int) { val offset = getMoveOffsetOrZero(index) if (offset == 0) return var position = index do { position += offset } while (position >= 0 && position < tiles.size && tiles[position] != 0) do { val next = position - offset tiles[position] = tiles[next] position = next } while (position != index) tiles[index] = 0 moves += 1 } fun tileAt(index: Int) = tiles[index] companion object { const val GRID_COUNT = 16 fun generate(): PuzzleState { val tiles = IntArray(GRID_COUNT) { it } // Shuffle (generate only solvable configurations) do { tiles.shuffle(Random.Default) } while (!isSolvable(tiles)) return PuzzleState(tiles) } private fun isSolvable(tiles: IntArray): Boolean { val inversions = (0..15).sumOf { idx -> (idx + 1 until tiles.size).count { tiles[idx] != 0 && tiles[it] != 0 && tiles[idx] > tiles[it] } } // Solvable if blank is on odd row (from bottom) and inversions are even, or blank is on even row and inversions are odd return (3 - tiles.indexOf(0) / 4) % 2 == inversions % 2 } } } 2.1. Define a State that Holds Coordinate (Offset) State In the following code, we convert targetOffset from the original PuzzleGrid(...) using animateValueAsState(...) to define a new animatedOffset , and replace the arguments of Modifier.offset(...) in PuzzleTile(...) . animatedOffset represents the same offset as targetOffset while having the capability to smoothly express changes in offset from before to after tile movement. However, in this example, these 12 lines of code alone don't animate; we needed to use the variable tilePositions that sorts the display order, as commented with // Track current position of each tile (by value) . This sorting ensures that the order of tiles in the for loop within PuzzleGrid(...) is always kept in order of the numbers on the tiles. By doing this, under Jetpack Compose's declarative UI concept, we ensure expression continuity by always identifying tiles 1-15 as the same before and after movement, enabling animation display (we hoped the label argument of animateValueAsState would identify composable identity, but this effect was not confirmed in androidx.compose.animation library 1.10.0. Also, using key(...) to identify composables was considered, but this also doesn't seem to work): @Composable private fun PuzzleGrid( puzzleState: PuzzleState, modifier: Modifier = Modifier, onTileClick: (Int) -> Unit ) { // Track current position of each tile (by value) val tilePositions = IntArray(16) { -1 } repeat(PuzzleState.GRID_COUNT) { index -> tilePositions[puzzleState.tileAt(index)] = index } BoxWithConstraints( modifier = modifier .fillMaxWidth() .aspectRatio(1F) ) { val gridSize = maxWidth val tileSize = (gridSize - 12.dp) / 4 // 3 gaps of 4dp // Draw tiles except blank (for each value 1-15) for (value in 1.until(PuzzleState.GRID_COUNT)) { val position = tilePositions[value] if (position == -1) continue val targetOffset = DpOffset( x = (tileSize + 4.dp) * (position % 4), y = (tileSize + 4.dp) * (position / 4) ) val animatedOffset by animateValueAsState( targetValue = targetOffset, typeConverter = TwoWayConverter( convertToVector = { AnimationVector2D(it.x.value, it.y.value) }, convertFromVector = { DpOffset(Dp(it.v1), Dp(it.v2)) } ), animationSpec = spring( dampingRatio = Spring.DampingRatioNoBouncy, stiffness = Spring.StiffnessMedium ), label = "tile_$value" ) PuzzleTile( value = value, onClick = { onTileClick(position) }, modifier = Modifier .offset(x = animatedOffset.x, y = animatedOffset.y) .size(tileSize) ) } } } Nonetheless, with the addition of about a dozen lines of code and a few changes, we were able to express animation while keeping the code structure mostly intact. To customize this animation, try changing the typeConverter , animationSpec , and other arguments of animateValueAsState(...) . I'll continue introducing techniques for efficiently adding animations in the following sections. ![パズルのアニメヌション䞭](/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_puzzle.webp =256x) Fig. 1: Puzzle animating 3. Adding Animation for Show/Hide Transitions In the puzzle game above, the display when solving the puzzle is rather plain, so we'd like to add some flair. That said, since it's a mini-game, something modest will do. For now, let's consider an effect where something pops out when the puzzle is solved. There's a simple method prepared for this. 3.1. Wrap with AnimatedVisibility(...) and Add Modifier.animateEnterExit(...) Inside Simply changing if (solved) { ... } to AnimatedVisibility (solved) { ... } enables default animations when transitioning from hidden to visible and from visible to hidden. To change the default animation, add animateEnterExit(...) to the Modifier of each composable wrapped by AnimatedVisibility(solved) { ... } : @Composable fun Puzzle15(modifier: Modifier = Modifier) { // ... Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) { // ... AnimatedVisibility(solved) { Text( text = "🎉 Solved! 🎉", modifier = Modifier.animateEnterExit( enter = scaleIn( animationSpec = spring( dampingRatio = Spring.DampingRatioHighBouncy, stiffness = Spring.StiffnessMedium ) ), exit = None ), style = solvedStyle ) } } } The enter and exit of Modifier.animateEnterExit(...) set the animations for transitioning from hidden to visible and from visible to hidden, respectively. The default values are set to fadeIn() and fadeOut() , respectively. In this case, since we only want to set animation when appearing, we set None for exit so it disappears without animation. Wrapping a composable with an upper-level composable to create a scope ( AnimatedVisibilityScope ) and adding individual settings to the Modifier of wrapped individual composables is a frequently used technique in Jetpack Compose. Similar techniques will appear later. 4. Expressing Animation Where Old Screen Disappears and New Screen Appears on Screen Update The following code represents the action of moving on a 2D integer coordinate plane by tapping arrow buttons for up, down, left, and right: @Composable fun FlatField(modifier: Modifier = Modifier) { var xy by remember { mutableStateOf(Coordinates2D(0, 0)) } Box( modifier = Modifier .fillMaxSize() .background(xy.background) .safeContentPadding() ) { IconButton( modifier = modifier.align(Alignment.CenterStart), onClick = { xy = xy.goLeft() } ) { Icon( modifier = Modifier.size(64.dp), imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "goLeft", tint = xy.foreground ) } IconButton( modifier = Modifier.align(Alignment.TopCenter), onClick = { xy = xy.goUp() } ) { Icon( modifier = Modifier .size(64.dp) .rotate(90F), imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "goUp", tint = xy.foreground ) } IconButton( modifier = Modifier.align(Alignment.CenterEnd), onClick = { xy = xy.goRight() } ) { Icon( modifier = Modifier.size(64.dp), imageVector = Icons.AutoMirrored.Filled.ArrowForward, contentDescription = "goRight", tint = xy.foreground ) } IconButton( modifier = Modifier.align(Alignment.BottomCenter), onClick = { xy = xy.goDown() } ) { Icon( modifier = Modifier .size(64.dp) .rotate(90F), imageVector = Icons.AutoMirrored.Filled.ArrowForward, contentDescription = "goDown", tint = xy.foreground ) } Text( text = xy.coordinateString, modifier = Modifier.align(Alignment.Center), color = xy.foreground, fontSize = 64.sp ) } } You can repeat operations like moving from the initial white location to the red location via the down button, then to the blue location via the right button, and so on, but without motion, it's difficult to grasp the sense of movement. This is exactly when adding animation is effective. The class representing the state during movement is as follows: data class Coordinates2D(val x: Int, val y: Int) { val background: Color val foreground: Color val coordinateString: String init { val index = nonNegativeRemainder() background = backgroundColors[index] foreground = foregroundColors[index] coordinateString = coordinateString(x, y) } private fun nonNegativeRemainder(): Int = ((x + y) % 3).let { if (it < 0) it + 3 else it } fun goLeft() = Coordinates2D(x - 1, y) fun goUp() = Coordinates2D(x, y - 1) fun goRight() = Coordinates2D(x + 1, y) fun goDown() = Coordinates2D(x, y + 1) companion object Companion { private val backgroundColors = arrayOf(Color.White, Color(0xED, 0x29, 0x39), Color(0x00, 0x23, 0x95)) private val foregroundColors = arrayOf(Color.Black, Color.White, Color.White) private fun coordinateString(x: Int, y: Int) = if (x == 0 && y == 0) "O" else "(${x}, ${y})" } } 4.1. Wrap with AnimatedContent(...) and Add Animation for Transitioning from Old Screen to New Screen The following wraps the Box(...) inside FlatField(Modifier) with AnimatedContent(...) , but since the animation definition requires information about the size of Box(...) , we change Box(...) to BoxWithConstraints(...) , reference constraints , and use the values of maxWidth and maxHeight as arguments to AnimatedContent(...) : @Composable fun FlatField(modifier: Modifier = Modifier) { var xy by remember { mutableStateOf(Coordinates2D(0, 0)) } var width by remember { mutableIntStateOf(0) } var height by remember { mutableIntStateOf(0) } AnimatedContent( modifier = modifier.fillMaxSize(), targetState = xy, transitionSpec = { val deltaX = targetState.x - initialState.x val deltaY = targetState.y - initialState.y slideIn { IntOffset(x = deltaX * width, y = deltaY * height) } togetherWith slideOut { IntOffset(x = -deltaX * width, y = -deltaY * height) } }, label = "coordinates2D" ) { targetXy -> BoxWithConstraints( modifier = Modifier .fillMaxSize() .background(targetXy.background) .safeContentPadding() ) { width = constraints.maxWidth height = constraints.maxHeight IconButton( modifier = Modifier.align(Alignment.CenterStart), onClick = { xy = targetXy.goLeft() } ) { Icon( modifier = Modifier.size(64.dp), imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "goLeft", tint = targetXy.foreground ) } IconButton( modifier = Modifier.align(Alignment.TopCenter), onClick = { xy = targetXy.goUp() } ) { Icon( modifier = Modifier .size(64.dp) .rotate(90F), imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "goUp", tint = targetXy.foreground ) } IconButton( modifier = Modifier.align(Alignment.CenterEnd), onClick = { xy = targetXy.goRight() } ) { Icon( modifier = Modifier.size(64.dp), imageVector = Icons.AutoMirrored.Filled.ArrowForward, contentDescription = "goRight", tint = targetXy.foreground ) } IconButton( modifier = Modifier.align(Alignment.BottomCenter), onClick = { xy = targetXy.goDown() } ) { Icon( modifier = Modifier .size(64.dp) .rotate(90F), imageVector = Icons.AutoMirrored.Filled.ArrowForward, contentDescription = "goDown", tint = targetXy.foreground ) } Text( text = targetXy.coordinateString, modifier = Modifier.align(Alignment.Center), color = targetXy.foreground, fontSize = 64.sp ) } } } In the transitionSpec argument of AnimatedContent(...) , we define the animation by merging slideOut { ... } that pushes out the old screen and slideIn { ... } that brings in the new screen using infix EnterTransition.togetherWith(ExitTransition) . The argument ( targetXy ) of the lambda expression given to the content argument of AnimatedContent(...) holds the state of the screen being pushed out, and the state is updated by assigning the new state obtained from targetXy to xy in the onClick of each IconButton(...) . ![アニメヌションによる疑䌌スクロヌル](/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_pseudo-scroll_1.webp =256x) ![アニメヌションによる疑䌌スクロヌル](/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_pseudo-scroll_2.webp =256x) Fig. 2-1, 2-2: Pseudo-scroll by animation With this code, if you (forcibly build while ignoring compiler warnings by) writing xy = xy.goLeft() instead of using targetXy , the old screen being pushed out during animation won't display correctly. This writing style shows that AnimatedContent(...) correctly manages the state during animation. To customize this animation, try changing the transitionSpec argument of AnimatedContent(...) , etc. Adding animation makes it much easier to grasp the sense of position movement. Looking at this motion, it seems this could also be used as a two-dimensional pager. Jetpack Compose provides HorizontalPager for horizontal scrolling and VerticalPager for vertical scrolling, but there's no standard two-dimensional pager. However, you can create a pager-like UI just by adding animation. Of course, since we've only added animation, you can't swipe to see part of an adjacent page, but with just about a dozen lines of additions and some changes, you can significantly change the app's impression. 5. Animation Connecting Before and After Screen Transitions in NavHost(...) Starting from Compose Animation version 1.10.0 , SharedTransitionLayout became stable. This defines shared elements for screens created with composables. What are shared elements? Check the short video in Shared element transitions in Compose . Here's a code example when using NavHost(...) : private val colorMap = mapOf( "èµ€" to Color.Red, "緑" to Color.Green, "青" to Color.Blue, "シアン" to Color.Cyan, "マれンタ" to Color.Magenta, "黄" to Color.Yellow, "茶" to Color(132, 74, 43), "矀青" to Color(76, 108, 179), "カヌキヌ" to Color(197, 160, 90) ) @Composable fun GridTransform(modifier: Modifier = Modifier) { val navController = rememberNavController() NavHost( modifier = modifier .safeContentPadding() .fillMaxSize(), navController = navController, startDestination = ROUTE_SMALL_SQUARE ) { composable(route = ROUTE_SMALL_SQUARE) { val onClick: (String) -> Unit = { navController.navigate("$ROUTE_LARGE_SQUARE?$ARG_SHARED=$it") } Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { Column( modifier = Modifier .aspectRatio(1f) .padding(8.dp) .fillMaxSize() .background( color = MaterialTheme.colorScheme.primaryContainer, shape = RoundedCornerShape(16.dp) ) ) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { ColorButton( "èµ€", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( "緑", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( "青", modifier = Modifier.weight(1f), onClick = onClick ) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { ColorButton( "シアン", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( "マれンタ", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( "黄", modifier = Modifier.weight(1f), onClick = onClick ) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { ColorButton( "茶", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( "矀青", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( "カヌキヌ", modifier = Modifier.weight(1f), onClick = onClick ) } } } } composable( route = "$ROUTE_LARGE_SQUARE?$ARG_SHARED={sharedKey}", arguments = listOf(navArgument("sharedKey") { type = NavType.StringType }) ) { entry -> val colorName = entry.arguments?.getString("sharedKey") ?: "" LargeSquare(colorName) { navController.popBackStack() } } } } @Composable fun ColorButton( colorName: String, modifier: Modifier = Modifier, onClick: (String) -> Unit ) { TextButton( modifier = modifier .padding(16.dp) .aspectRatio(1f) .background(color = colorMap[colorName]!!, shape = RoundedCornerShape(16.dp)), onClick = { onClick(colorName) } ) { Text(colorName, color = Color.White) } } @Composable private fun LargeSquare( colorName: String, modifier: Modifier = Modifier, onBack: () -> Unit ) { Box( modifier = modifier .padding(16.dp) .fillMaxSize() .aspectRatio(1f) .background(color = colorMap[colorName]!!, shape = RoundedCornerShape(16.dp)) .clickable { onBack() } ) { Text(text = colorName, modifier = Modifier.padding(8.dp), color = Color.White) } } Note: This code omits countermeasures against rapid tapping of the back button and 9-color buttons for simplicity. Please test while avoiding rapid tapping in short intervals. On the initial screen, 9 color buttons are displayed, and tapping a button shows a large square in the tapped button's color. When the large square is displayed, pressing the back button (or performing a back gesture) returns to the 9-color button screen. Here, you can see fade-in and fade-out animations when returning to the large square screen by button tap and when returning to the 9-color button screen by back button. This is specified by the default values of the enterTransition , exitTransition , popEnterTransition , popExitTransition , sizeTransform arguments of NavHost(...) . You can override this setting with animation that associates shared elements before and after specific screen transitions by adding code and making modifications as shown below for specific screen transitions. For implementing shared element animation in screen transitions that don't involve back stack changes via NavHost(...) , refer to Shared element transitions in Compose . During screen transitions from left to right and right to left in the table below, the animation visually expresses that the square on the right screen that appeared from the button tapped on the left screen (bottom right) is exactly the target content. 9-color button screen Large square screen ![9色のボタンの画面](/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_9-buttons.webp =256x) Fig. 3-1 ![Large square screen](/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_large-square.webp =256x) Fig. 3-2 5.1. Wrap with SharedTransitionLayout { ... } and Share Screen Elements Here we introduce the implementation of shared element animation before and after screen transitions via NavHost(...) : @Composable fun GridTransform(modifier: Modifier = Modifier) { val navController = rememberNavController() SharedTransitionLayout { NavHost( modifier = modifier .safeContentPadding() .fillMaxSize(), navController = navController, startDestination = ROUTE_SMALL_SQUARE ) { composable(route = ROUTE_SMALL_SQUARE) { val onClick: (String) -> Unit = { navController.navigate("$ROUTE_LARGE_SQUARE?$ARG_SHARED=$it") } Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { Column( modifier = Modifier .aspectRatio(1f) .padding(8.dp) .fillMaxSize() .background( color = MaterialTheme.colorScheme.primaryContainer, shape = RoundedCornerShape(16.dp) ) ) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { ColorButton( this@composable, "èµ€", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( this@composable, "緑", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( this@composable, "青", modifier = Modifier.weight(1f), onClick = onClick ) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { ColorButton( this@composable, "シアン", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( this@composable, "マれンタ", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( this@composable, "黄", modifier = Modifier.weight(1f), onClick = onClick ) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { ColorButton( this@composable, "茶", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( this@composable, "矀青", modifier = Modifier.weight(1f), onClick = onClick ) ColorButton( this@composable, "カヌキヌ", modifier = Modifier.weight(1f), onClick = onClick ) } } } } composable( route = "$ROUTE_LARGE_SQUARE?$ARG_SHARED={sharedKey}", arguments = listOf(navArgument("sharedKey") { type = NavType.StringType }) ) { entry -> val colorName = entry.arguments?.getString("sharedKey") ?: "" LargeSquare(this, colorName) { navController.popBackStack() } } } } } @Composable fun SharedTransitionScope.ColorButton( animatedContentScope: AnimatedVisibilityScope, colorName: String, modifier: Modifier = Modifier, onClick: (String) -> Unit ) { TextButton( modifier = modifier .padding(16.dp) .aspectRatio(1f) .background(color = colorMap[colorName]!!, shape = RoundedCornerShape(16.dp)) .sharedBounds( sharedContentState = rememberSharedContentState(colorName), animatedVisibilityScope = animatedContentScope, boundsTransform = { _, _ -> tween(durationMillis = 500) }, enter = fadeIn(), exit = fadeOut(), resizeMode = SharedTransitionScope.ResizeMode.scaleToBounds() ), onClick = { onClick(colorName) } ) { Text(colorName, color = Color.White) } } @Composable private fun SharedTransitionScope.LargeSquare( animatedContentScope: AnimatedVisibilityScope, colorName: String, modifier: Modifier = Modifier, onBack: () -> Unit ) { Box( modifier = modifier .padding(16.dp) .fillMaxSize() .aspectRatio(1f) .background(color = colorMap[colorName]!!, shape = RoundedCornerShape(16.dp)) .sharedBounds( sharedContentState = rememberSharedContentState(colorName), animatedVisibilityScope = animatedContentScope, boundsTransform = { _, _ -> tween(durationMillis = 500) }, enter = fadeIn(), exit = fadeOut(), resizeMode = SharedTransitionScope.ResizeMode.scaleToBounds() ) .clickable { onBack() } ) { Text(text = colorName, modifier = Modifier.padding(8.dp), color = Color.White) } } Wrapping the outer layer with SharedTransitionLayout { ... } and using Modifier.sharedBounds(...) within SharedTransitionScope to associate shared elements is the same as for screen transitions that don't use NavHost(...) . For the animatedVisibilityScope argument of Modifier.sharedBounds(...) , we use AnimatedContentScope ( this@composable ) derived from NavGraphBuilder.composable(...) . This enables displaying animation between shared elements during screen transitions via NavHost(...) . In SharedTransitionScope.ColorButton(...) and SharedTransitionScope.LargeSquare(...) , verify that screen elements are shared before and after screen transitions by using the button's color name as the key argument of rememberSharedContentState(Any) . The new coding required to set up shared element animation is: Wrap with SharedTransitionLayout { ... } Set sharedBounds(...) (or sharedElement(...) ) to define shared elements and match the key between shared elements Pass the two scopes SharedTransitionScope and AnimatedVisibilityScope required for 2. to the composable These can be implemented without significantly changing the structure of the code before animation settings. If it's difficult to apply with minimal changes to existing code, try refactoring to a structure that's easier to modify. Shared element animation is beautiful when it fits well, but animation that perfectly matches the screen design image may be difficult. In that case, try adjusting the enter , exit , boundsTransform , resizeMode arguments of Modifier.sharedBounds(...) , etc. 5.2. Relationship with Predictive Back Shared element animation overrides the default screen transition animation of NavGraphBuilder.composable(...) . Additionally, when predictive back animation is enabled, that is, when android:enableOnBackInvokedCallback="true" is specified in AndroidManifest.xml for API levels 33-35, or when android:enableOnBackInvokedCallback="false" is not specified in AndroidManifest.xml for API level 36 or higher, it also overrides the back animation via NavHost(...) . Build the app with predictive back animation enabled, set the device to gesture navigation mode, and slowly perform the back gesture on the large square screen above, and you can easily confirm that the shared element animation slowly reverses. Also, on API level 36 or higher and Android OS 16 or higher, setting to button navigation mode and long-pressing the back button should show the shared element reverse animation. Thus, the back animation of NavHost(...) affects the predictive back animation settings. With this in mind, you need to decide whether to enable predictive back animation settings. As of API level 36, you can disable predictive back animation by specifying android:enableOnBackInvokedCallback="false" . 6. Conclusion This article introduced 4 examples of techniques for implementing practical animations in Jetpack Compose with minimal changes. Animation within apps is not essential functionality in most cases, so implementation tends to be omitted especially in development projects with tight schedules. But depending on usage, it has the potential to bring significant usability improvements and greatly enhance the app's impression. If there are abundant means to achieve this with minimal effort, you can easily try implementation. Many APIs in Compose Animation are designed to be declarative , meaning you can add animations with the feeling of declaring that you'll animate UI elements, making it easy to avoid the complexity of having to define behavior through detailed procedural descriptions. I hope these can be utilized to improve the quality of many apps. 7. References Android API reference Quick guide to Animations in Compose Animation modifiers and composables Add support for predictive back animations
この蚘事は KINTOテクノロゞヌズ Advent Calendar 2025 の8日目の蚘事です🎅🎄 はじめに こんにちは。ご芧いただきありがずうございたす KINTO FACTORY にお、フロント゚ンド開発しおいる䞭本です。 今回は、技術寄りのお話ではなく今幎月に、息子が誕生した際に取埗した育児䌑暇に぀いお、珟堎レベルでの匕き継ぎや埩垰しおからの感想など、玹介させおいただきたす。 䞊叞ぞの盞談 今幎の月より、FACTORY開発グルヌプ内のフロント゚ンドチヌムのチヌムリヌダヌにアサむン頂いたので、最初は長期間で䌑むこずに少し抵抗も感じたしたが、マネヌゞャヌぞ盞談した際には快く育児䌑暇の取埗を埌抌し頂きたした。 特に産たれおからの最初の数カ月は、奥さんも心身ずもに倧倉な時期になるず思うので、なるべくサポヌトしおあげおください、ず前向きな蚀葉をかけお頂きたした。 そこで、ひずたずヶ月ほどの育児䌑暇を申請し、ヶ月経った頃に埩垰できそうかどうかを刀断させおいただく、ずいうやり方にさせお頂きたした。 匕き継ぎ事項 さお、日々の業務を匕き継ぎしおいくにあたり、普段やっおいるフロント゚ンド開発に加え、チヌムリヌダヌずしお、 新芏案件のアヌキテクチャ確認・方向性決定・関係郚眲ずの連携 各メンバヌずの 1-on-1 各メンバヌず半期ごずの振り返りず評䟡 採甚掻動 などがありたした。 䞀぀ず぀、どのようにしおいったか深堀りたす。 新芏案件のアヌキテクチャ確認・方向性決定・関係郚眲ずの連携 䌑暇取埗が始たりそうな、月埌半〜にかけおのロヌドマップをPdMやマネヌゞャヌず日々確認したずころ、そこたで倧きな新芏案件は無さそうで、珟圚進行䞭のものが倧半でした。 そこで、今たで自分の方で担圓しおいた案件は、極力 Confluence の方にもたずめるこずを始め、「誰ずどのような話をしおここたで決たっおいる」や「自分の䞭でここたではやった」などを簡単にたずめるクセを付けおおきたした。 各メンバヌずの 1-on-1 各メンバヌず半期ごずの振り返りず評䟡 次に、マネゞメント゚リアです。 チヌムリヌダヌの぀のミッションずしお、半期ごずに各メンバヌの振り返りを確認し評䟡する必芁がありたす。 その振り返り期間が、ちょうど䌑暇を取埗する期間ずモロ被りしそうだったので、あらかじめ月の期が始たったタむミングから、各自に目暙を蚭定しおもらい振り返りをスムヌズにできるようにしおおきたした。 実際には、各メンバヌが決めた目暙ずヶ月ごずに「䜕を達成した、来月は䜕をする」をConfluence ぞ蚘入しおもらい、1-on-1の話のネタにしおおりたした。 自分の方でも、気付いた郚分・頑匵っおくれた点は䞊蚘 Confluence ぞメモしおおき、振り返りの際にフィヌドバックできるようにしおおきたした。 産たれるタむミングで䌑暇を取埗する予定だったため、もし評䟡期間ず被っおしたった堎合は、䞊蚘 Confluence をそのたたマネヌゞャヌぞお枡しすれば、各メンバヌの目暙ず成果をすぐ確認できるず考えおおりたした。 採甚掻動 こちらは、基本的にマネヌゞャヌぞ代わりをお願いさせお頂きたした。 ただ、開発メンバヌにも面接時から応募者の技術力やコミュニケヌション力などを感じお欲しいので、面接に同行しおもらい、珟堎レベルでのコミュニケヌションやテックスキルなどを確認しおもらいたした。 おやすみ䞭 さお、そのように倧䜓の匕き継ぎ目凊が経った8月24日、無事に元気な男の子が産たれたした そこから日間の特別䌑暇を頂き、圹所関係や病院ずの埀埩を繰り返し、育児䌑暇開始前日に䌚瀟からの貞䞎品PC、スマホ、瀟員蚌などを返华しに日だけ出瀟したした。 ここから玄ヶ月匱の間、業務からは䞀歩匕いお、育児にフルコミットするこずずしたす 育児は倧倉 FACTORY開発グルヌプには、珟圹の子育おパパがいっぱいいるので、色々なお話を産前から䌺っおたしたが、䟋にも挏れずやはり最初のヶ月は倧倉でした... 我が家は、劻ず自分で半日ず぀の亀代制で育児ず睡眠を分け、自分は朝起きおから倜寝るたでの育児を担圓したした。劻が、そのほうが育児䌑暇明けも睡眠サむクルを厩さず埩垰できるのではず蚀っおくれたので、基本的に日䞭は起きお・倜間に就寝できるようになりたした。 ミルク・おむ぀替え・あやす・沐济など、やるこずが分単䜍でやっおくるので、自分のこずを考える䜙裕もなく、最初の1-2週間は業務のこずは党く頭になかったかず思いたす (Žω`) でもサヌビスも気になる 䌑暇に入っお、ヶ月ほど経ったくらいで、マネヌゞャヌずお話しでき、案件の進捗状況やチヌムの状態など共有いただくこずができたした。ヶ月間、倖出も最䜎限で、他人ずお話するのも久しぶりだったので、粟神的にもすごく解攟された感じがしたした。 たた、この頃からだいぶ育児にも慣れお来おおり、息子も萜ち着いお寝る時間が増えおきたので、ちょこちょこサヌビスの曎新状況を芋に行っおいた蚘憶がありたす。 KINTO FACTORYは氎曜日に新商品の発売開始や、新機胜の提䟛が始たるので、氎曜日はサむトに蚪れ「今月は䜕がでたかな」ず確認しおいたした。 åž°é‚„ そしおこの床、65日間の育児䌑暇を終え11月より珟堎に埩垰したした。 グルヌプのメンバヌなどにも倧きな倉化はなく、進行䞭だった案件が埌ろ倒しになっおいたりず、思っおいたより䌑暇に入る前からそこたで倧幅な倉化もなく、すんなりず埩垰するこずができた印象です。 ただ、いきなり劻が日䞭垯でのワンオペ育児ずなるので、なるべく負荷を䜎枛しおもらうために、できるだけ朝早い時間に出瀟し倕方もなるべく早く垰宅するように心がけおいたす。 このあたりも、フルフレックス制床によりMTGの調敎やチヌム内での合意を取れれば、自由に皌働時間を調敎するこずが可胜ずなっおおりたす。 さいごに 最初のヶ月間育児に専念するこずで、息子が初めお芋せた笑顔だったり、あヌうヌず唞りだしたりず、日に日に成長する姿を間近に芋るこずができ、この時期に育児に関わるこずで貎重な䜓隓をするこずができたず思いたす。 このような䜓隓ができたのも、育児䌑暇を取埗するにあたっお、快く送り出しお頂いたマネヌゞャヌ、そしおグルヌプの皆さんにこの堎を借りお感謝させお頂きたいです。 KINTO テクノロゞヌズでは、このように男性でも育児䌑暇を取埗しやすい環境かず思いたす。実際に、今幎だけでも他郚眲含め倚くの男性゚ンゞニアが育児䌑暇を取埗しおいるお話を聞いおいたす瀟内に息子の同玚生も倚い。 育児䌑暇を取埗する準備や、埩垰に぀いお少しでも参考になれば幞いです。 たた、䞋蚘蚘事では同じFACTORY開発グルヌプ内の先茩パパの日が玹介されおいたすので、そちらもぜひご芧頂ければず思いたす パパママ゚ンゞニア必芋KTCパパ゚ンゞニアの日