歴史あるプロジェクトのとある技術的負債を隙間プロジェクトの 210 PullRequests で倒しきった話 2022.10.21. Kaigi on Rails 2022
ྺ࢙͋ΔϓϩδΣΫτͷͱ͋Δٕज़తෛ࠴Λ伱ؒϓϩδΣΫτͷ1VMM3FRVFTUTͰ͖ͬͨ͠2022.10.21.Kaigi on Rails 2022@makicamel
View Slide
w!NBLJDBNFMݪສقw3VCZͱϏʔϧɹɹͱ͓ञ͕͖w͖ͳ73ήʔϜwࣗݾհࠓͷ
ٕज़తෛ࠴͋Γ·͔͢ʁ
ٕज़తෛ࠴ฦͯ͠·͔͢ʁ
ٕज़తෛ࠴ͷฦ٫•ܭըΛཱͯͯઓུతʹ͢ͷ͕ηΦϦʔ•ͱݴ͑ޙճ͠ɾ์ஔ͞Εͯརଉ͕ΒΈ͕ͪ•։࢝ൺֱత༰қ•ܧଓɾқߴେ͖ͳ
ͰɺΓ͍ͨ
ྺ࢙͋ΔϓϩδΣΫτͷͱ͋Δٕज़తෛ࠴Λ伱ؒϓϩδΣΫτͷ1VMM3FRVFTUTͰ͖ͬͨ͠
•7 ؒେ͖͘ҭͬͨ Rails ΞϓϦ•ेਓ͕৮ΔϓϩδΣΫτྺ࢙͋ΔϓϩδΣΫτ+- ---------------------+- --------+- -------+- --------+- -------- +- ---- +- ------ +| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |+- ---------------------+- --------+- -------+- --------+- -------- +- ---- +- ------ +| Controllers | 106768 | 84571 | 1182 | 8419 | 7 | 8 || Jobs | 2532 | 1869 | 96 | 139 | 1 | 11 || Models | 173665 | 125931 | 834 | 9068 | 10 | 11 || Mailers | 515 | 444 | 10 | 11 | 1 | 38 || Libraries | 4464 | 3134 | 37 | 313 | 8 | 8 || … | … | … | … | … | … | … |+- ---------------------+- --------+- -------+- --------+- -------- +- ---- +- ------ +| Total | 1112716 | 885175 | 2169 | 18354 | 8 | 46 |+- ---------------------+- --------+- -------+- --------+- -------- +- ---- +- ------ +
•ෳࡶͳઃܭɾ࣮•ਆςʔϒϧɾਆϞσϧɾڊେίϯτϩʔϥɾڊେείʔϓ…•ΦϨΦϨϑϨʔϜϫʔΫ•etc …Έ߹ΘͤͰΑΓෳࡶԽٕज़తෛ࠴
•։ൃॳظΛࢧ͑ͨΦϨΦϨϑϨʔϜϫʔΫ•ΞϓϦͷʹ͋Θͤͨϝϯς͕͞Εͣෛ࠴Խ•ANDPAD ͷͭΒΈτοϓϥϯΧʔ•2019 ळɿࣾهࣄͰ՝ͱհ•2020 य़ɿఫഇͷखॱॻ͕ॻ͔ΕΔղফਐ·ͣCrudController
class UsersController < ApplicationControllerbefore_action :new_one, only: [:new, :create]def new; enddef createif @user.savecreate_success_response_toelsecreate_error_response_toendenddef new_one@user = scope.new(one_params)enddef create_success_response_torespond_to do |format|format.html {flash[:notify_success] = "࡞͠·ͨ͠"redirect_to { action: :index }}format.json {render json: @user.to_json}endenddef create_error_response_torespond_to do |format|format.html {flash[:notify_error] = "࡞Ͱ͖·ͤΜͰͨ͠"# …͜͏ৼΔ͏2 ߦॻ͚ͩ͘Ͱclass UsersController < ApplicationControllerinclude CrudControllercrud_controller User, [:new, :create]end
•include ͢ΔͱΫϥεϝιου crud_controller ͕ੜ͑Δ•ΞΫγϣϯϝιουΛΑ͠ͳʹఆٛͯ͘͠ΕΔ•search_one, scope ͳͲΑ͠ͳʹϝιου͕ੜ͑Δ•ϝιουΛΦʔόʔϥΠυͯ͠ϨεϙϯεΛΧελϜͰ͖ΔCrudController
•λΠϓܹݮʂ•ΜΓʂʂ…΄Μͱ͏ʹʁ͔ΜͨΜ
ͭΒΈྫ
ɹϝιουఆ͕ٛͳ͍ͷʹͳΜ͔ಈ͘ʂʁ҉తͳϝιουఆٛ class UsersController < ApplicationControllercrud_controller User, [:new, :create]end
ɹ͜ͷ @user ҰମͲ͔͜Βʂʁ҉తͳίʔϧόοΫՃclass UsersController < ApplicationControllercrud_controller User, [:new, :create]def createif @user.savecreate_success_response_toelsecreate_error_response_toendendend
ϝιουίʔϧࢄɹશ෦ಡ·ͳ͍ͱΘ͔Βͳ͍ 💀class UsersController < ApplicationControllerinclude Common::UsersControllerinclude ActionStateinclude ActionForminclude ActionUpdateinclude ActionApidef set_redirect_path@create_redirect_path = users_pathendendmodule Common::UsersControllerextend ActiveSupport::Concernincluded docrud_controller User, [:edit, :update]before_action :set_redirect_pathendend
ɹCrudController ͷίʔυಡ͏ͱͨ͠ΜͰ͚͢Ͳ ɹϝλϓϩΑ͘Θ͔Γ·ͤΜ…ϝλϓϩdef define_create_error_response_to(_clazz, _table_name, model_name)define_method :create_error_response_to dorespond_to do |format|format.html {flash[:notify_error] = "࡞Ͱ͖·ͤΜͰͨ͠"create_error_response_to_html}format.json {create_error_response_to_json}endenddefine_method :create_error_response_to_html dorender self.class.instance_variable_get(:@_create_error_action) || :newenddefine_method :create_error_response_to_json dorender json: { errors: instance_variable_get("@#{model_name}").errors.full_messages }, status: 422endend
ෳࡶ•όάΛੜΈ͍͢•ίʔϧόοΫॱংʹґଘ͕ൃੜ͢Δ•੬ऑੑΛੜΈ͍͢ঢ়ଶ•CrudController ֶशίετ͕ߴ͍•ϝλϓϩͰՄಡੑ͕͍•ೝίετ͕ߴ͍•ϝιουͷΦʔόʔϥΠυͰ࣮ࡍͷఆ͕ٛΘ͔ΓͮΒ͍•Fat controller ͱΈ߹Θ͞ΔͱΑΓΩπ͍
େྔ•ར༻ίϯτϩʔϥ 300 ऑ
͖͠ΔͨΊʹ•εΫϦϓτԽ•มߋϦεΫΛݮΒ͢•νʔϜઓʹ͢Δ•ϞνϕʔγϣϯΛอͭ
εΫϦϓτԽ•ख࡞ۀͷఫഇແཧ•࣌ؒͱࠜؾͱूதྗ͕ඞཁ•͋͘·Ͱ伱ؒ࣌ؒͷऔΓΈ•ఫഇखॱॻ͕ॻ͚ΔͳΒεΫϦϓτԽͰ͖Δ
ɹॻ͍ͨ
εΫϦϓτ1. crud_controller .*ΞΫγϣϯ໊ ΛؚΉϑΝΠϧΛநग़2. 1 ͷ͏ͪରԠ͢Δςετ͕͋ΔϑΝΠϧΛநग़3. ίϯτϩʔϥϑΝΠϧΛจࣈྻͱͯ͠ಡΈࠐΈɺ ϝιουͷಈతఆٛΛ੩తఆٛʹมߋ4. 3 Ͱมߋͨ͠จࣈྻΛίϯτϩʔϥϑΝΠϧʹॻ͖͢5. rubocop -a ͯ͠ίϛοτΛੵΉ جຊ͜Ε͚ͩ
εΫϦϓτ1. crud_controller .*ΞΫγϣϯ໊ ΛؚΉϑΝΠϧΛநग़όοΫΫΦʔτͰ ίϚϯυ࣮ߦ
1. 1 ͷ͏ͪରԠ͢Δςετ͕͋ΔϑΝΠϧΛநग़2. 1 ͷ͏ͪରԠ͢Δςετ͕͋ΔϑΝΠϧΛநग़εΫϦϓτίϯτϩʔϥͷ Ϋϥε໊Λऔಘ
1. 1 ͷ͏ͪରԠ͢Δςετ͕͋ΔϑΝΠϧΛநग़2. 1 ͷ͏ͪରԠ͢Δςετ͕͋ΔϑΝΠϧΛநग़εΫϦϓτΫϥεʹରԠ͢Δ spec ͕͋Δ ίϯτϩʔϥΛબͿ
1. 1 ͷ͏ͪରԠ͢Δςετ͕͋ΔϑΝΠϧΛநग़2. 1 ͷ͏ͪରԠ͢Δςετ͕͋ΔϑΝΠϧΛநग़3. ίϯτϩʔϥϑΝΠϧΛจࣈྻͱͯ͠ಡΈࠐΈɺ ϝιουͷಈతఆٛΛ੩తఆٛʹมߋεΫϦϓτFile.read ͯ͠ จࣈྻͱͯ͠ѻ͏
1. 1 ͷ͏ͪରԠ͢Δςετ͕͋ΔϑΝΠϧΛநग़2. 1 ͷ͏ͪରԠ͢Δςετ͕͋ΔϑΝΠϧΛநग़3. ίϯτϩʔϥϑΝΠϧΛจࣈྻͱͯ͠ಡΈࠐΈɺ ϝιουͷಈతఆٛΛ੩తఆٛʹมߋεΫϦϓτจࣈྻΛΓషΓ
2. 1 ͷ͏ͪରԠ͢Δςετ͕͋ΔϑΝΠϧΛநग़3. .rb ϑΝΠϧΛจࣈྻͱͯ͠ಡΈࠐΈɺ4. 3 Ͱมߋͨ͠จࣈྻΛίϯτϩʔϥϑΝΠϧʹॻ͖͢εΫϦϓτFile.write Ͱ্ॻ͖
3. .rb ϑΝΠϧΛจࣈྻͱͯ͠ಡΈࠐΈɺ4. 3 Ͱมߋͨ͠จࣈྻΛ .rb ϑΝΠϧʹॻ͖͢5. rubocop -a ͯ͠ίϛοτΛੵΉεΫϦϓτ
εΫϦϓτԽΧελϜ Cop Λ࡞ͬͯ autocorrect ͢Δͷྑͦ͞͏
ܧଓతͳෛ࠴ฦ٫•伱ؒ࣌ؒͰߦ͑ΔΑ͏ʹ͢Δ•ίϚϯυΛଧ͚ͭͩͰ diff ͷ࡞͕ྃ•͓खܰ•ॻ͍ͨ௨Γಈ͘•ूதྗෆཁMTG લʹ 10 ۭ͍ͨ࣌ͱ͔
ܧଓతͳෛ࠴ฦ٫GitHubActions ͳͲͰ PR ͷ࡞·Ͱ ࣗಈԽ͢ΔͷΑͦ͞͏
•εΫϦϓτʹ͢Δͱ࡞ۀखॱΛΕΒΕΔ•͍ͭͰΊΒΕΔ•ຊۀ͕͘͠ͳͬͨ࣌•͍ͭͰ࠶։Ͱ͖Δ•ϋʔυϧ͕͍࣮ࡍ్தͰ 1 ऑ์ஔͨ͠ܧଓతͳෛ࠴ฦ٫
มߋϦεΫΛݮΒ͢•ϘʔΠεΧτɾϧʔϧϘʔΠεΧτɾϧʔϧcϓϩάϥϚ͕Δ͖ͷ͜ͱ IUUQTϓϩάϥϚ͕Δ͖ͷ͜ͱDPNΤοηΠϘʔΠεΧτϧʔϧlj✣ǟƎw㢟ŧx҆ٳƵҗźƂơơƎżƮŶƋƒa NJǵƵŰƬżƑƋŻŲƬŧa ഠ߶֥Ǝ൳ŴೆƯůƂŧŶƋƊżb ⅅŮƬᾖƉƥwźƉƒƍƬƍŧŶƋxƍƑƊżb
•ϘʔΠεΧτɾϧʔϧΛकΒͳ͍ϘʔΠεΧτɾϧʔϧcϓϩάϥϚ͕Δ͖ͷ͜ͱ IUUQTϓϩάϥϚ͕Δ͖ͷ͜ͱDPNΤοηΠϘʔΠεΧτϧʔϧlj✣ǟƎw㢟ŧx҆ٳƵҗźƂơơƎżƮŶƋƒa NJǵƵŰƬżƑƋŻŲƬŧa ഠ߶֥Ǝ൳ŴೆƯůƂŧŶƋƊżb ⅅŮƬᾖƉƥwźƉƒƍƬƍŧŶƋxƍƑƊżbมߋϦεΫΛݮΒ͢
มߋͷؔ৺ࣄΛগͳ͘อͭ
crud_controller User, [:update] ͢Δͱ search_one ͕ੜ͑ΔΠϯελϯεมͷηοτ update ͕Αͦ͞͏class UsersController < ApplicationControllercrud_controller User, [:update]before_action :search_one, only: [:update]before_action :update_requireddef updateif @user.update(one_params)# ...else# ...endenddef search_one@user = scope.find(params[:id])enddef update_requiredraise UnauthorizedError unless @user.can_update?endendมߋͷؔ৺ࣄΛগͳ͘อͭ
search_one ΛΠϯϥΠϯʹͨ͠●ɹ●class UsersController < ApplicationControllercrud_controller User, [:update]before_action :update_requireddef update@user = scope.find(params[:id])if @user.update(one_params)# ...else# ...endenddef update_requiredraise UnauthorizedError unless @user.can_update?endendมߋͷؔ৺ࣄΛগͳ͘อͭ
@user ͕ nil ʹͳͬͯΤϥʔʹͳͬͨclass UsersController < ApplicationControllercrud_controller User, [:update]before_action :update_requireddef update@user = scope.find(params[:id])if @user.update(one_params)# ...else# ...endenddef update_requiredraise UnauthorizedError unless @user.can_update?endendมߋͷؔ৺ࣄΛগͳ͘อͭ💀
•ίʔυ࣭ʹؔ৺͕͋Δਓ΄Ͳ͍ͭͰͰͯ͠͠·͍͕ͪ•ؔ৺ࣄ͕૿͑Δͱόάͷࠞೖʹؾ͖ͮʹ͘͘ͳΔ࣍ͷมߋͰͦ͏มߋͷؔ৺ࣄΛগͳ͘อͭ
มߋϦεΫΛݮΒ͢
มߋϦεΫΛݮΒ͢োΛىͨ͜͠
มߋϦεΫΛݮΒ͔ͨ͢͠ʹมߋྔ ଟ͔͚ͬͨͲςετ͕͋ͬͨͷʹ Ͳ͏ͯ͠
ِӄੑςετexpect ͕ͳͯ͘ݕͰ͖ͳ͔ͬͨRSpec.describe 'PUT /users/:id' do# …it doput user_url(user), params: paramsendendԿςετͯ͠ͳ͍ʂʂʂ
ِӄੑςετ•ʮςετ͕͋Δ != ςετ͍ͯ͠Δʯঢ়ଶͩͬͨ•expectation ͷͳ͍ it Λݕ͢ΔΧελϜ Cop Λ࡞ͬͨ[email protected]@DPQ [email protected]@DPQ"EEA34QFD/P&YQFDUBUJPO&YBNQMFACZSLBNVSBu1VMM3FRVFTU IUUQTHJUIVCDPNSVCPDPQSVCPDPQSTQFDQVMM@r7kamura ͞ΜͷύονͰ rubocop-rspec ʹऔΓࠐ·Ε 2.13.0 ͔Βར༻Ͱ͖·͢
•PR Λখ͘͢͞Δͷجຊ•ϨϏϡʔ͘͢͢͠ΔͨΊมߋྔΛগͳ͘อͭ
•ػցతͳมߋ PR Λׂ͖͔͢ʁ•҆શੑͱίετͷόϥϯε•ࠓճ҆શੑΛͱͬͨ•োϞνϕʔγϣϯΛԼ͛ΔมߋྔΛগͳ͘อͭςετ͕ͳ͍ॴ͋Γ ࣌ʑख࡞ۀ͋ͬͨͷͰ
•҆શͳมߋΛ્͢ΔཁҼΛಓʹഉআ͢Δ•ϨϏϡʔΞϒϧʹ͢Δ•ؔ৺ࣄΛগͳ͘อͭ•มߋΛগͳ͘อͭ•ِӄੑͷςετΛ͙มߋϦεΫΛݮΒ͢
νʔϜઓʹ͢Δ•🙅 ίʔυ࣭ʹؔ৺͕ߴ͍ਓʹϨϏϡʔґཔ•🙆ίʔυͷΦʔφʔͬΆ͍νʔϜʹϨϏϡʔґཔ•ෛ࠴ฦ٫͕ʮίʔυ࣭ʹؔ৺͕ߴ͍ͱݟΒΕ͍ͯΔਓʯͷ ؔ৺ࣄʹͳΔ•ෛ࠴ΛʮΈΜͳͷؔ৺ࣄʯʹ͢Δ
νʔϜઓʹ͢Δrake λεΫʹͯ͠୭ͰऔΓΊΔΑ͏ʹ͢Δ
νʔϜઓʹ͢Δ•ϝϯόʔ͕खΛڍ͛ͯ͘Εͯ 2 ਓͰऔΓΉ͜ͱʹ•ϨϏϡʔ͕രͰճΔ•ਐΊํͷվળఏҊͯ͘͠ΕͯഒͰఫഇ͕ਐΉ•ਓͱҰॹʹΔͱָ͍͠
ϞνϕʔγϣϯΛอͭ•͖Δ•׆ಈͷҙٛʹٙΛ࣋ͪ࢝ΊΔ͏ࠔ͍ͬͯΔਓ ͍ͳ͍ͷͰʁ
ϞνϕʔγϣϯΛอͭ•ਐḿΛݟ͑ΔԽ͢Δ•Կ % ऴΘ͔ͬͨࣈڧ͍
ϞνϕʔγϣϯΛอͭ•๙ΊͯΒ͏
๙ΊͯΒ͏
๙ΊͯΒ͏ڵຯؔ৺Λ ࣋ͬͯ͘ΕͯͨΜͩͳ͊։ൃຊ෦ࣗຫେձͰ CrudController ఫഇྃΛࣗຫͨ࣌͠ͷԠ
👋
·ͱΊ҆શʹɾָ͘͠Ͱ͖Δํ๏Λ୳͢ܧଓੑ
)BQQZ)BDLJOH
͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠